未完待续…
链表的问题不难,就是需要考虑很多边界条件。。
需要注意的是:
1.当连边的结构发生改变时时,可能当前遍历的节点指针p->next指针已经不指向之前的位置了,一般可以设置多个指针来完成遍历。
2.当头结点不确定时,或者是为了避免删除头结点的麻烦,在head节点之前再加一个节点是一个很重要的技巧,否则还要判断新的head节点是否为NULL。
3.删除节点/改变节点结构:可以看做 新建立 一个链表,这样只要把一个或者一串节点接在新链表tail指针之后即可。
4.别忘记判断head为NULL;
不要忘记链表最后要为NULL;
链表遍历过程中,也要注意判NULL,特别是设置多个指针来完成遍历的时候;
注意链表结构的变化,画图即可!!!
1.遍历链表(删除节点,改变链表结构)
1.1删除节点:
19. 删除链表的倒数第N个节点
1.在head节点之前再加一个节点,让一个指针先走n+1步,找到第链表的倒数第 n+1 个节点slow,这样即使一共有n个节点,也不会出错
2.删除slow之后的节点即可
或者:
1.fast先走n步,如果fast此时==NULL,说明删除头结点
2.否则找到del之前的节点即可
ListNode* removeNthFromEnd(ListNode* head, int n) {
//1.在head节点之前加一个节点nhead
//2.找到del节点之前的节点
int k=n+1; while(k--) fast=fast->next;
while(fast){
fast=fast->next;
slow=slow->next;
}
//3.delete del;
//4.return nhead->next;
}
83. 删除排序链表中的重复元素(只保留一个)
1.相邻两个元素(p->val和q->val)不相等说明不重复,此时连接节点;
2.如果重复,就删除重复节点q。
注意:这里的p既可以做为连接操作,也可以作为val比较。
ListNode* deleteDuplicates(ListNode* head) {
ListNode* p=head, *q=head->next;
while(q){
if(p->val!=q->val){ //1.相邻两个元素不相等说明p->val不重复
p->next=q;//1.1中间如果删除过节点,那么要重新决定p->next指向下一个不重复节点
}
else//2.如果是重复值的节点,就删除q节点
delete del;
}
p->next=NULL;//3.串起来最后别忘记NULL;
}
82. 删除排序链表中的重复元素 II(只保留没有重复出现的数字)
1.因为可能会删除头结点,因此在head节点之前加一个nhead节点,上面那个题就不会删除头结点。
2.如果p->next && p->val==p->next->val,就while删除所有相同的值。一定要注意p ||p->next为空的情况,是不能得到指针指向的值的
3.如果不满足2,则接在ntail后面。
//1.在head节点之前加一个节点nhead
while(p){
if(p->next && p->val==p->next->val){//2.在遍历过程中,p有重复值注意判断指针为空的情况
int tmpval=p->val;
while(p && p->val==tmpval){//2.1删除所有重复值,注意判断指针为空的情况
ListNode* del=p;
p=p->next;
delete del;
}
}else{//3.如果不是重复值,则接在ntail后面
ntail->next=p;
ntail=ntail->next;
p=p->next;
}
}
ntail->next=NULL;
return nhead->next;
203. 删除链表中的值为val的节点
1.自己创建一个头结点,放在head节点之前
2.while(p)
(1)如果p->val==val则delete p,否则接在ntail之后
(2)p=q;q=q->next;
//1.在head节点之前加一个节点nhead;*p=head,*q=p->next;
while(p){
if(p->val==val)//2.如果p->val==val则delete p
delete p;
else{//3.接在ntail之后
ntail->next=p;
ntail=ntail->next;
}
p=q;
if(p) q=q->next;
}
ntail->next=NULL;
return nhead->next;
237. 删除链表中的给定的节点
把下一个节点的值赋给这个节点,删除下一个界定就好啊。
void deleteNode(ListNode* node) {
if(!node || !node->next)
return;
ListNode * tmp = node->next;
node->val = tmp->val;
node->next = tmp->next;
delete tmp;
return;
}
1.2 插入/组合/分离链表
21. 合并两个有序链表
1.while(l1&&l2),需要注意的是,由于链表连接结构的改变,我们需要两个指针来做链表移动。
(1)val小的做连接操作,val小的后移;
(2)如果相等,解连接两个val,然后l1,l2均后移
2.最后连接上不为空的链表;如果都是空,别忘记:rTail->next=NULL;
while(l1 && l2)
{
if(l1->val<l2->val)
l1接在Tail之后;
else if(l2->val<l1->val)
l2接在Tail之后;
else
两个节点都接上;
}
if(!l1 && !l2)//如果都是空
Tail->next=NULL;
if(l1)//l1不为空,l2空
Tail->next=l1;
else if(l2)//l2不为空,l1空
Tail->next=l2;
86. 分隔链表(使得所有小于 x 的节点都在大于或等于 x 的节点之前。)
开2个新链表,最后在连接在一起即可,前加节点真的好用的很,不用再额外讨论head节点在哪?!!!
while(p)
{
//1.申请两个新的头节点
if(p->val<x)//(1)小于x则接在nhead之后
{
ntail->next=p;
ntail=ntail->next;
}
else//(2)否则接在另外一个链表之后
{
tail->next=p;
tail=tail->next;
}
p=p->next;//2.遍历
}
tail->next=NULL;//大于等于x值得信链表最后要加NULL
ntail->next=np->next;//3.连接
return nhead->next;
138. 复制带随机指针的链表
很好的一道题,综合插入,组合,分离,
1.先new新节点放在每个节点之后,插入
2.设置random指针,组合
3.分离,分出两个链表
RandomListNode *copyRandomList(RandomListNode *head) {
if(!head)
return NULL;
RandomListNode* pre=head, *late=NULL;
RandomListNode* now=NULL;
while(pre)//1
{
now=new RandomListNode(pre->label);
now->next=pre->next;
pre->next=now;
pre=now->next;
}
pre=head;now=pre->next;
while(pre)//2
{
if(pre->random)
now->random=pre->random->next;
pre=now->next;
if(pre)
now=pre->next;
}
pre=head;
RandomListNode* nhead=pre->next;//保留返回值(new head)
now=nhead;
late=now->next;
while(late)//3
{
pre->next=late;
now->next=late->next;
pre=late;
now=pre->next;
late=now->next;
}
pre->next=NULL;
return nhead;
}
1.3交换、反转,旋转,回文(重要!!!)
24. 两两交换链表中相邻的节点(给定 1->2->3->4, 你应该返回 2->1->4->3)
1.这种改变链表结构的题可以看做生成新的链表
2.在while中分两种情况讨论:p,q相邻的两个节点,且每次移动两个位置
(1)节点个数为单数,提前return;
(2)节点个数为双数,记得ntail->next=NULL;
3.然后return;
//1.前加nhead节点
while(p)
{
if(!p->next)//2.1.最后,节点个数为单数,提前return;
{
ntail->next=p;
return nhead->next;
}
//2.2.节点个数为双数,交换即可
}
ntail->next=NULL;//3.别忘记空节点
return nhead->next;//4.nhead->next
61. 旋转链表(最后k个节点串挪到前面)
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
//1.求长度len,防止k>len而不知,k %= len
//2.构造循环链表,tail(尾节点)->next=head;
if(k %= len) ////3.k>len时!!!
for(int i=0; i<len-k; i++)
nTail = nTail->next;
//这里注意要从 ‘tail’ 开始,这样可以保证无论k是否等于len,都能正确找到newhead!!!
nHead = nTail->next; //4.找到了新的head节点;有时候,找到head前一个节点是一个比较好的选择。
nTail->next = NULL;
206. 反转链表
p=NULL, *now=head, *late=now->next三个节点遍历即可,注意late的判NULL。
92. 反转链表 II(反转从第 m 到第 n 个节点之间的链表。请使用一趟扫描完成反转。)
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
提前构造一个节点放在head之前,这样就可以避免从第一个对象开始就不好处理!!!
最重要的还是画图!!!!
//backtail为尾后指针
ListNode* reverseij(ListNode* head,ListNode* backtail);
ListNode* reverseBetween(ListNode* head, int m, int n) {
ListNode* nhead=reverseij(p,q);//1.反转
preTail->next=nhead;//2.连接前面的
p->next=q;//3.连接后面的
return nhead->next;
}
234. 判断回文链表
需要求中间节点和反转的子函数!!!
ListNode* reverse(ListNode* head);
ListNode* midfun(ListNode* slow);
bool isPalindrome(ListNode* head) {
while(head&&rhead){//从头和从尾双向遍历,如果两个val不相等return false;最后才返回true
if(head->val!=rhead->val)
return false;
head=head->next;
rhead=rhead->next;
}
}
1.4 数第N个节点,判环,入环点,相交(重要!!!)
19. 删除链表的倒数第N个节点
*1.为了避免删除头结点的麻烦,在head节点之后再加一个节点
*2.让一个指针先走n+1步,找到第链表的倒数第 n+1 个节点slow,这样即使一共有n个节点,也不会出错
*3.删除slow之后的节点即可
或者:
*1.fast先走n步,如果fast此时==NULL,说明删除头结点
*2.否则找到del之前的节点即可
141. 判断环形链表
快慢节点判断链表中是否有环,注意:先移动后比较,因为
再来一遍就找到如入环点
while(fast->next && fast->next->next){
fast=fast->next->next;//1.先移动,后比较
slow=slow->next;
if(slow==fast)//2.后比较
return true;
}
return false;
142. 环形链表 II(返回入环的第一个节点)
注意:“ if (!fast->next || !fast->next->next) return NULL; ”
以此判断是否有环
while(fast->next && fast->next->next){
fast=fast->next->next;
slow=slow->next;
if(slow==fast)
break;//1.1有环就break,退出while循环,但是保存的fast节点后面会用到
}//1.2没有环也会退出while循环...
//2.判断是否有环如果 fast->next || !fast->next->next 为NULL,说明无环,
if (!fast->next || !fast->next->next)
return NULL;//2.1无环返回NULL
//2.2有环
slow=head;
while(fast && slow)//2.2.1找到入环点
{
if(fast==slow)
return slow;
slow=slow->next;
fast=fast->next;
}
return NULL;
160. 相交无环单链表(有环或者不确定有没有环呢?此时需要先找到入环点是不是NULL?!!)
1.链表长度分别为m,n,长的先走abs(m-n)步
2.然后再一起走,直到一个为空,判断就好了
1.5——2. 两数相加
给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
1.while(p&&q),直接相加并且记录标志位即可;退出循环时,要看两个链表是否均为NULL,并且如果flag是1,不要发忘记new ListNode(flag)
2.如果两个链表不等长,此时长的链表继续。。
3.如果标志位式中为1,并且p不为空,while继续计算;否则:若p不为空,但是flag为0,直接接上;若p为空,但是flag为1,new ListNode(flag)
4.p为空且flag为0,直接返回res->next。
2.排序相关
109. 有序链表转换二叉搜索树
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
1.开数组,把节点放在里面
2.取中点,构造高度平衡的二叉搜索树,
[两种用数组构造AVL的方法均正确]
TreeNode* sortedListToBST(ListNode* head) {
//法一:v.size()-1
return sortedArrayToBST(v,0,v.size()-1);
//法二:1.改为:v.size()
return sortedArrayToBST(v,0,v.size());
}
TreeNode* sortedArrayToBST(vector<int>& v, int i, int j){
if(i > j)
return NULL;
//找到中间的作为每次根节点,保证平衡,然后先序构建即可
int mid=(i+j)/2;
TreeNode* root=new TreeNode(v[mid]);
root->left=sortedArrayToBST(v, i, mid-1);//法二:2.改为:v, i, mid
root->right=sortedArrayToBST(v, mid+1,j);
return root;
}
147. 对链表进行插入排序
这里写代码片
148. 归并排序链表
这里写代码片
总结:
1.遍历链表(删除节点,改变链表结构)
1.1删除节点:
19.删除链表的倒数第N个节点
83.删除排序链表中的重复元素(只保留一个)
82.删除排序链表中的重复元素 II(只保留没有重复出现的数字)
203.删除链表中的值为val的节点
237.删除链表中的给定的节点
1.2 插入/组合/分离链表
21.合并两个有序链表
86.分隔链表(使得所有小于 x 的节点都在大于或等于 x 的节点之前。)
138.复制带随机指针的链表
1.3交换、反转,旋转(重要!!!)
24.两两交换链表中相邻的节点(给定 1->2->3->4, 你应该返回 2->1->4->3)
61.旋转链表(最后k个节点串挪到前面)
206.反转链表
92.反转链表 II(反转从第 m 到第 n 个节点之间的链表。请使用一趟扫描完成反转。)
234.判断回文链表
1.4 数第N个节点,判环,入环点,相交(重要!!!)
19.删除链表的倒数第N个节点
141.判断环形链表
142.环形链表 II(返回入环的第一个节点)
160.相交无环单链表(有环或者不确定有没有环呢?此时需要先找到入环点是不是NULL?!!)
1.5——2. 两数相加
2.排序相关
109.有序链表转换二叉搜索树
147.对链表进行插入排序
148.归并排序链表