leetcode笔记之链表

未完待续…
链表的问题不难,就是需要考虑很多边界条件。。
需要注意的是:
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.归并排序链表

猜你喜欢

转载自blog.csdn.net/qiangzhenyi1207/article/details/79991933