剑指 Offer 24. 反转链表
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情。
思路
(双指针,迭代) (n)
反转链表即将所有节点的next指针指向他的前驱节点。由于是单链表,我们在迭代时不能直接找到前驱节点,所以我们需要一个额外的指针保存前驱节点。同时在改变当前节点的next指针前,不要忘记保存它的后继节点。
具体过程如下:
- 1、定义一个前驱指针
pre和cur指针,pre指针用来指向前驱节点,cur指针用来遍历整个链表,初始化cur = head,pre = null。 - 2、首先让
t指针指向cur指向节点的后继节点,然后让cur指向节点的next指针指向其前驱节点,即cur->next = pre。 - 3、
pre指针和cur指针分别后移一位,重复上述过程,直到cur指向空节点。 - 4、最后我们返回
pre节点。
时间复杂度分析: 只遍历一次链表,时间复杂度是O(n)。
c++代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution
public:
ListNode* reverseList(ListNode* head) {
ListNode *pre = NULL;
ListNode *cur = head;
while(cur)
{
ListNode *t = cur->next; //保留后继节点
cur->next = pre; //当前节点指向前驱节点
pre = cur, cur = t;
}
return pre;
}
};
剑指 Offer 25. 合并两个排序的链表 *
思路
(二路归并) O(n)
1、新建头部的虚拟头节点dummy,设置cur指针指向dummy。
2、若当前l1指针指向的节点的值val比l2指针指向的节点的值val小,则令cur的next指针指向l1,且l1后移一位;否则指向l2,且l2后移一位。
3、然后cur指针按照上一步设置好的位置后移。
4、循环以上步骤直到l1或l2为空。
5、将剩余的l1或l2接到cur指针后边。
时间复杂度分析: 两个链表各遍历一次,所以时间复杂度为O(n)
c++代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* dummy = new ListNode(-1);
ListNode* cur = dummy;
while(l1 != NULL && l2 != NULL){
if(l1->val < l2->val){
cur->next = l1;
l1 = l1->next;
}else{
cur->next = l2;
l2 = l2->next;
}
cur = cur->next;
}
cur->next = (l1 != NULL ? l1 : l2); //将剩余的l1或l2接到cur指针后边
return dummy->next;
}
};
剑指 Offer 26. 树的子结构 *
(二叉树,递归) O(nm)
我们首先判断两棵二叉树是否为空,如果一个为空,则直接返回false。遍历树A中的所有非空节点root,判断树A中以root为根节点的子树是不是包含和树B一样的结构,且我们从根节点开始匹配,当从A的根节点开始不匹配的话,我们递归到A的左右子树去匹配。
isSame函数用来判断判断B是否为A的子树,具体设计思路如下:
- 如果树
B中的节点为空,则表示当前分支是匹配的,返回true; - 如果树
A中的节点为空,但树B中的节点不为空,则说明不匹配,返回false; - 如果两个节点都不为空,但数值不同,则说明不匹配,返回
false; - 否则说明当前这个点是匹配的,然后递归判断左子树和右子树是否分别匹配即可;
时间复杂度分析: 最坏情况下,我们对于树A中的每个节点都要递归判断一遍,每次判断在最坏情况下需要遍历完树B中的所有节点。 所以时间复杂度是 O(nm),其中 n 树A中的节点数, m 是树B中的节点数。
c++代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isSubStructure(TreeNode* A, TreeNode* B) {
if(!A || !B) return false; //如果A树或者B树一个为空,则不匹配
if(isSame(A, B)) return true; // 从根节点开始判断,找到第一个匹配的位置
// 如果根节点不匹配的话,我们递归到左右子树去判断
return isSubStructure(A->left, B) || isSubStructure(A->right, B);
}
bool isSame(TreeNode* A, TreeNode* B){
if(!B) return true; //B树为空,匹配成功
// B树不为空,若A树为空,或者A,B都不为空,但是值不相等,匹配不成功
if(!A || A->val != B->val) return false;
//否则说明当前这个点是匹配的,然后递归判断左子树和右子树是否分别匹配即可
return isSame(A->left, B->left) && isSame(A->right, B->right);
}
};