代码随想录链表练习

目录

(一)移除链表元素

(二)设计链表

(三)翻转链表

(四)两两交换链表中的节点

(五)删除链表倒数第n个节点

(六)链表相交

(七)环形链表Ⅱ

(一)移除链表元素

第一种是头结点需要单独处理的情况:


 //处理头结点
    while(head!=0&&head->val==val){
      ListNode *p=head;
      head=head->next;
      delete p;
	//处理其他节点
	ListNode *q=head;
	while(q!=0&&q->next!=0){
		if(q->next->val==val){
		    ListNode *r=q->next;
			q->next=q->next->next;
			delete r;
		}
		else
		   q=q->next;

第二种是用虚拟头结点。相当于在原有链表前再添加一个模拟节点:


ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
        dummyHead->next = head; // 将虚拟头结点指向head,这样方便后面做删除操作
        ListNode* cur = dummyHead;
        while (cur->next != NULL) {
            if(cur->next->val == val) {
                ListNode* tmp = cur->next;
                cur->next = cur->next->next;
                delete tmp;
            } else {
                cur = cur->next;
            }
        }
        head = dummyHead->next;
        delete dummyHead;
        return head;

(二)设计链表

内容很多但不难,注意private用法*


class MyLinkedList {
public:
     struct LinkedNode {
        int val;
        LinkedNode* next;
        LinkedNode(int val):val(val), next(nullptr){}
    };

    // 初始化链表
    MyLinkedList() {
        _dummyHead = new LinkedNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
        _size = 0;
    }

    // 获取到第index个节点数值,如果index是非法数值直接返回-1, 注意index是从0开始的,第0个节点就是头结点
    int get(int index) {
        if (index > (_size - 1) || index < 0) {
            return -1;
        }
        LinkedNode* cur = _dummyHead->next;
        while(index--){ 
            cur = cur->next;
        }
        return cur->val;
    }

    // 在链表最前面插入一个节点,插入完成后,新插入的节点为链表的新的头结点
    void addAtHead(int val) {
        LinkedNode* newNode = new LinkedNode(val);
        newNode->next = _dummyHead->next;
        _dummyHead->next = newNode;
        _size++;
    }

    // 在链表最后面添加一个节点
    void addAtTail(int val) {
        LinkedNode* newNode = new LinkedNode(val);
        LinkedNode* cur = _dummyHead;
        while(cur->next != nullptr){
            cur = cur->next;
        }
        cur->next = newNode;
        _size++;
    }

    void addAtIndex(int index, int val) {

        if(index > _size) return;
        if(index < 0) index = 0;        
        LinkedNode* newNode = new LinkedNode(val);
        LinkedNode* cur = _dummyHead;
        while(index--) {
            cur = cur->next;
        }
        newNode->next = cur->next;
        cur->next = newNode;
        _size++;
    }

    // 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的
    void deleteAtIndex(int index) {
        if (index >= _size || index < 0) {
            return;
        }
        LinkedNode* cur = _dummyHead;
        while(index--) {
            cur = cur ->next;
        }
        LinkedNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;

        tmp=nullptr;
        _size--;
    }

    // 打印链表
    void printLinkedList() {
        LinkedNode* cur = _dummyHead;
        while (cur->next != nullptr) {
            cout << cur->next->val << " ";
            cur = cur->next;
        }
        cout << endl;
    }
private:
    int _size;
    LinkedNode* _dummyHead;
};

 (三)翻转链表

     思路一:双指针

     pre指向cur的前一位,目的是为了翻转后cur向前指。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
     ListNode *p=NULL;
     ListNode*cur=head;
     ListNode*mid;
     while(cur!=NULL){//不是cur->next!=NULL因为这样会漏掉最后一个值
         mid=cur->next;
         cur->next=p;
         p=cur;
         cur=mid;
     }
     return p;//cur只会返回一个值,p有完整的链表
    }
};

思路二:递归的想法(本质还是双指针)

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
      return reverse(NULL,head);
    }
    ListNode* reverse(ListNode *p,ListNode*cur){
       ListNode*mid;
       if(cur==NULL)  {return p;}
       mid=cur->next;
       cur->next=p;
      return  reverse(cur,mid);
    }
};

思路三:头插法(需要建立虚拟头结点)

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
     ListNode *dummyHead=new ListNode(-1);
     ListNode *cur=head;
     while(cur!=NULL){
       ListNode *mid=cur->next;
       cur->next=dummyHead->next;
       dummyHead->next=cur;
       cur=mid;
     }
     return dummyHead->next;
    }
};

思路四:用栈的特性(这里老报错,不知道为啥)

 Stack<ListNode> stack = new Stack<>();

(四)两两交换链表中的节点(*)

这边画个图就能理解了,说实话第一次看代码给绕晕了。。。

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
    ListNode*dummyHead=new ListNode(0);
    dummyHead->next=head;
    ListNode *cur=dummyHead;
    while(cur->next!=NULL&&cur->next->next!=NULL){
       ListNode *temp=cur->next;
       ListNode *temp1=cur->next->next->next;
       cur->next=cur->next->next;
       cur->next->next=temp;
       cur->next->next->next=temp1;
       cur=cur->next->next;
    }
    return dummyHead->next;
    }
};

(五)删除链表倒数第n个节点

        注意slow需要指向被删节点前一个节点,所以要n++。若在后面写fast=fast->next可能会出问题(当n超过区间范围时)

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
    ListNode *dummyHead=new ListNode(0);
    dummyHead->next=head;
    ListNode *fast=dummyHead,*slow=dummyHead;
    n++;
    while(n--&&fast!=NULL){
        fast=fast->next;
    }
    while(fast!=NULL){
        fast=fast->next;
        slow=slow->next;
    }
    slow->next=slow->next->next;
    return dummyHead->next;
    }
};

(六)链表相交

        这里主要注意相同节点而不是相同数值

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *p=headA;
        ListNode *q=headB;
        int lenA=0,lenB=0;
        while(p!=NULL){
            lenA++;
            p=p->next;
        }
        while(q!=NULL){
            lenB++;
            q=q->next;
        }  
        //这里需要再将两节点再指回头结点遍历,长的先移
        p=headA;
        q=headB;
        if(lenA>lenB){//统一默认让b最长
           swap(lenA,lenB);
           swap(p,q);

        }
        int gap=lenB-lenA;
        while(gap--){
         q=q->next;
        }
        while(q!=NULL){
            if(p==q)
             { return q;}
            p=p->next;
            q=q->next;
        }
        return NULL;
    }
};

(七)环形链表Ⅱ

        设置快慢指针,快指针速度为2,满指针速度为1,这样当快与慢相遇时一定有个环。这里有个追速度的问题,可以视慢指针不动,快指针以速度1的速度不断追击慢指针,所以一定会追到。如果快指针速度为3可能就碰不到了。

slow=x+y//x是起点到回路开端的距离,y是回路开端到碰撞节点的距离;

fast=x+y+n(z+y)//z=碰撞节点到回路开端的另外半圆的距离;

可以根据速度,写出等式:

2*slow=fast

2(x+y)=x+y+n(z+y)

当n=1时,可以推出x=z;

得出关键的信息,碰撞节点就是回路的开端节点。

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *fast=head;
        ListNode *slow=head;
        int pos=0;
        while(fast!=NULL&&fast->next!=NULL){
            slow=slow->next;
            fast=fast->next->next;
            if(slow==fast){
                ListNode *index1=fast;
                ListNode *index2=head;
                while(index2!=index1){
                    index1=index1->next;
                    index2=index2->next;
                }
                return index2;
            }
        }
        return NULL;
           
    }
};

总结

记得回顾,有的容易忘

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值