链表—学习笔记

目录

知识点:

代码模板总结:

题:

参考文献:


知识点:

链表:通过指针串联在一起的线性结构,每个节点包含指针域数据域

类型: 单链表(每个节点指向下一个节点),双链表(每个节点指向前节点和后节点),循环链表 (链表首尾相连)。

删除和添加:时间复杂度是O(1),但是查找的时间复杂度是O(n)。与数组相反。

代码模板总结:

1、链表的定义及调用方式:

//定义
struct ListNode {
    int val; // 节点存储的元素
    ListNode *next; // 指向下一个节点的指针
    ListNode(int x) : val (x), next(NULL) {} // 节点的构造函数
};

//构造初始化节点
ListNode* head = new ListNode(5);

1、虚拟节点定义

当涉及到链表的删除操作时,我们可以设置一个虚拟节点,让其指向链表的头节点,这样就可以针对整个链表进行相同的删除操作,而不是单独处理头节点。

 //设置虚拟节点,虚拟节点指向头节点
   ListNode* dummyHead = new ListNode(0);
   dummyHead->next = head;
 //查询整个链表
   ListNode* cur = dummyHead;
   while (cur->next != NULL) {
  }
//删除虚拟节点
   head = dummyHead->next;
   delete dummyHead;
   return head;

错误: 用虚拟节点时,开始要将虚拟节点的next指向头节点。结束时,要设置头节点是虚拟节点的next,删除虚拟节点,返回头节点。

2、交换节点

有节点需要预留,有节点需要改变,

保存都是temp(临时) = cur(当前);换节点用cur = temp

/*保存节点的时候,temp……=cur……*/
   ListNode* temp = cur->next;
   ListNode* temp1 = cur->next->next;
   ListNode* temp2 = cur->next->next->next;
/*换结点的时候,cur……=temp……*/
   cur->next = temp1;
   cur->next->next = temp;
   cur->next->next->next = temp2;

3、查找节点操作

//求链表A的长度
    int lenA = 0;
    while(curA != NULL) {
        lenA++;
        curA = curA->next;
    }
//查找index处的节点
    LinkedNode* cur = dummyNode->next;
    while(index--) {
        cur = cur->next;
    }
//遍历当前链表(可能在末尾添加节点)
    LinkedNode* cur = dummyNode;
    while(cur->next!=NULL) {
        cur = cur->next;
    }
    cur->next = newNode;

题:

203. 移除链表元素 - 力扣(LeetCode) (leetcode-cn.com)

注意:1、c++删除节点操作中记得释放内存。

2、设置一个虚拟节点,可以将头节点和其他节点以相同方式移除。

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        //设置虚拟节点
        ListNode* dummyHead = new ListNode(0);
        //虚拟节点指向头节点
        dummyHead->next = head;
        //查询整个链表
        ListNode* cur = dummyHead;
        while (cur->next != NULL) {
            //如果是那个值
            if (cur->next->val == val) {
                ListNode* temp = cur->next;
                cur->next = cur->next->next;
                delete temp;
            } else {
                cur = cur->next;
            }
        }
        //删除虚拟节点
        head = dummyHead->next;
        delete dummyHead;
        return head;
    }
};

 707. 设计链表 - 力扣(LeetCode) (leetcode-cn.com)

class MyLinkedList {
public:
    //定义链表结构体,结构体是大驼峰,结构体尾部有分号
    struct LinkedNode {
        int val;
        LinkedNode* next;
        LinkedNode(int x):val(x), next(NULL){}
    };
    //初始化链表
    MyLinkedList() {
        //定义一个虚拟节点,不是真正的头节点
        dummyNode = new LinkedNode(0);
        size = 0;
    }
    //获取链表中第 index 个节点的值
    int get(int index) {
        //判断是否超出限制
        if(index > size - 1 || index < 0) {
            return -1;
        }
        LinkedNode* cur = dummyNode->next;
        //查询列表
        while(index--) {
            cur = cur->next;
        }
        return cur->val;
    }
    // 在链表最前面插入一个节点,插入完成后,新插入的节点为链表的新的头结点
    void addAtHead(int val) {
        //定义一个新节点
        LinkedNode* newNode = new LinkedNode(val);
        //指向虚拟节点后的头节点
        newNode->next = dummyNode->next;
        //虚拟节点指向该节点
        dummyNode->next = newNode;
        //长度++
        size++;
    }
    // 在链表最后面添加一个节点
    void addAtTail(int val) {
        //定义一个新节点
        LinkedNode* newNode = new LinkedNode(val);
        //查询当前链表
        LinkedNode* cur = dummyNode;
        while(cur->next!=NULL) {
            cur = cur->next;
        }
        cur->next = newNode;
        //长度++(切记)
        size++;
    }
    /*在链表中的第 index 个节点之前添加值为 val  的节点。
    如果 index 等于链表的长度,则该节点将附加到链表的末尾。
    如果 index 大于链表长度,则不会插入节点。
    如果 index 小于0,则在头部插入节点。*/
    void addAtIndex(int index, int val) {
        if(index > size) { return;}
        LinkedNode* newNode = new LinkedNode(val);
        LinkedNode* cur = dummyNode;
        while(index--) {
            cur = cur->next;
        }
        newNode->next = cur->next;
        cur->next = newNode;
        size++;
    }
    //如果索引 index 有效,则删除链表中的第 index 个节点。
    void deleteAtIndex(int index) {
        //判断是否有效,可以等于当前长度
        if(index >= size || index < 0) return;
        //定义虚拟节点
        //LinkedNode* dummyNode = newNode* dummyNode;
        //当前节点(第一处错误)
        LinkedNode* cur = dummyNode;
        //查找
        while (index--) {
            cur = cur->next;
        }
        //找到该节点
        LinkedNode* temp = cur->next;
        cur->next = temp->next; 
        //释放并减一
        delete temp;
        size--;
    }
    //打印链表
    void printLinkList() {
        //定义节点
        LinkedNode * cur = dummyNode;
        //查找
        while (cur->next != NULL) {
            cout << cur->next->val <<" ";
            cur = cur->next;
        }
        cout <<endl;
    }
//注意还定义了私有成员
private:
    int size;
    LinkedNode* dummyNode;
};

链表中很多基础比较重要 。

24. 两两交换链表中的节点 - 力扣(LeetCode) (leetcode-cn.com)

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyNode = new ListNode(0);
        dummyNode->next = head;
        ListNode* cur = dummyNode;
        while(cur->next != nullptr && cur->next->next != nullptr) {
            /*保存节点的时候,temp……=cur……*/
            // 保存临时1,2,3节点
            ListNode* temp = cur->next;
            ListNode* temp1 = cur->next->next;
            ListNode* temp2 = cur->next->next->next;
            /*换结点的时候,cur……=temp……*/
            //步骤一
            cur->next = temp1;
            //步骤二
            cur->next->next = temp;
            //步骤三
            cur->next->next->next = temp2;
            //后移两位
            cur = cur->next->next;
        }
        return dummyNode->next;
    }
};

像这种涉及到很多节点之间交换,需要存下来的,

保存都是temp(临时) = cur(当前);换节点用cur = temp。就会不出错。 

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode) (leetcode-cn.com)

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyNode = new ListNode(0);
        dummyNode->next = head;
        ListNode* cur = dummyNode;
        ListNode* temp = dummyNode;
        int size = 0; 
        //快指针走到头
        while(cur->next != 0) {
            size++;
            //慢指针比快指针少走n步
            if(size > n) {
                temp = temp->next;
            }
            cur = cur->next;
        }
        ListNode* temp2;
        temp2 = temp->next;
        temp->next = temp->next->next;
        delete temp2;
        return dummyNode->next;
    }
};

错误:返回头结点的时候不要return head,存在没有链表头节点消失的情况,用虚拟节点。 

面试题 02.07. 链表相交 - 力扣(LeetCode) (leetcode-cn.com)

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* curA = headA;
        ListNode* curB = headB;
        int lenA = 0, lenB = 0, dif = 0;
        //求链表A的长度
        while(curA != NULL) {
            lenA++;
            curA = curA->next;
        }
        //求链表B的长度
        while(curB != NULL) {
            lenB++;
            curB = curB->next;
        }
        curA = headA;
        curB = headB;
        //求长度差,假设最大的是A
        if(lenA < lenB) {
            swap(lenA, lenB);
            swap(curA, curB);
        }
        dif = lenA - lenB;
        //尾部位置对其,即较长的链表向前移动长度差
        while(dif--) {
            curA = curA->next;
        }
        //遍历数组
        while(curA != NULL) {
            if(curA == curB)
            return curA;
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
    }
};

错误:1、判断节点长度的时候,用cur->next会引起超出数组。

2、本题是两个节点相等,即指针相同,不是数值相同。

142. 环形链表 II - 力扣(LeetCode) (leetcode-cn.com)

判断是否有环:定义快慢指针,fast走两个节点,slow走一个节点,如果有环一定会在环内相遇。

相遇地点:从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。

ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != NULL && fast->next != NULL) {
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow) {
                ListNode* meet = fast;
                ListNode* start = head;
                while(meet != start) {
                    meet = meet->next;
                    start = start->next;
                }
                return meet;
            }
        }
        return NULL;
    }

错误:1、循环时,保证fastNode不是空节点,且fastNode->next也不是空节点。

2、让节点先移动,再判断,否则两个结点都是头节点,直接相等。 

参考文献:

代码随想录 (programmercarl.com)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值