二刷 链表.移除链表元素;链表增删改查;链表翻转;两两交换链表中的节点;删除链表的倒数第N个节点;链表相交;环形链表

理论基础:

链表定义:是一种通过指针串联在一起的线性结构,每一节点由两部分组成:数据域和指针域(存放指向下一节点的指针),最后一个节点的指针域指向null,链表的入口节点称为头节点:head

链表的类型:

单链表双链表:每一个链表有两个指针域,一个指向上一节点,一个指向下一节点
循环链表:解决约瑟夫环的问题

链表的存储方式:散乱分布,分布机制取决于操作系统的内存管理

链表的定义:

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

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

链表的操作

删除节点,删除D节点,需要将C节点的next指针 指向E节点,并手动释放D节点内存

添加节点

性能分析

数组 数据量固定,频繁查询,较少增删

链表 数据量不固定,频繁增删,较少查询


 203.移除链表元素

 给你一个链表的头节点和一个整数al,请你删除链表中所有满足Node.val==val的节点

struct ListNode{
    int val;
    ListNode *next;
    ListNode(int x) : val(x),next(nullptr){}
};

class Solution{
public:
    ListNode* removeElements(ListNode* head, int val){
        //设置虚拟头节点
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;//将虚拟头节点指向head
        ListNode* cur = dummyHead;
        //遍历
        while (cur->next != nullptr) {
            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;
    }
};

707. 设计链表

你可以选择使用单链表或者双链表,设计并实现自己的链表。

单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。

如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。

实现 MyLinkedList 类:

  • MyLinkedList() 初始化 MyLinkedList 对象。
  • int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
  • void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
  • void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
  • void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
  • void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。

主要意思:

  • 获取链表第index个节点的数值
  • 在链表的最前面插入一个节点
  • 在链表的最后面插入一个节点
  • 在链表第index个节点前面插入一个节点

要找到index的前一个节点,cur->next指向index

定义的虚拟头结点就能满足

  • 删除链表的第索引个节点
class MyLinkedList {
public:
    struct LinkedNode {
        int val;
        LinkedNode* next;
        LinkedNode(int val):val(val), next(nullptr){}
    };

    MyLinkedList() {
        dummyHead = new LinkedNode(0);
        size = 0;
    }
    
    int get(int index) {
        if (index < 0 || index > (size - 1)) return -1;
        //注意,要求的是cur的val,cur就不包括dummyHead了
        LinkedNode* cur = dummyHead->next;
        while (index--) {
            cur = cur->next;
        }
        return cur->val;
    }
    
    void addAtHead(int val) {
        LinkedNode* newNode = new LinkedNode(val);
        LinkedNode* cur = dummyHead;
        newNode->next = cur->next;
        cur->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++;
    }
    
    void deleteAtIndex(int index) {
        if (index < 0 || index > (size - 1)) return;
        LinkedNode* cur = dummyHead; 
        while (index--) {
            cur = cur->next;
        }
        //暂存要删除的index
        LinkedNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        //删除后要赋值,否则就会变成野指针val随机,对下一次调用造成麻烦
        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;
};

206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* cur = head;
        ListNode* pre = nullptr;
        while(cur) {
            //保存cur的下一节点
            ListNode* tmp = cur->next;
            //开始翻转
            cur->next = pre;
            //更新
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
};

19.删除链表的倒数第N个节点

双指针,fast先走n+1步,这样能保证fast走到null后,slow走到倒数第n个节点的前一个

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        //采用快慢指针:fast先走n+1步,fast走到null后,slow走到倒数第n个前一位。
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* fast = dummyHead;
        ListNode* slow = dummyHead;
        while (n-- && fast != nullptr) {
            fast = fast ->next;
        }
        fast = fast->next;
        while (fast != nullptr) {
            fast = fast->next;
            slow = slow->next;
        }
        //暂存
        ListNode* temp = slow->next;
        slow->next = slow->next->next;
        delete temp;
        return dummyHead->next;
    }

};

面试题 02.07. 链表相交

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

注意:求两个链表交点节点的指针。交点不是数值相等,而是指针相等。

1.curA指向链表A的头节点;carB指向链表B的头节点

2.求出两个链表的长度,并求出两个链表长度的差值,curA移动到和curB末尾对齐的位置 

3.此时比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,找到交点

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* curA = headA;
        ListNode* curB = headB;
        //求链表长度
        int lenA = 0, lenB = 0;
        while (curA != NULL){
            lenA++;
            curA = curA->next;
        }
        while (curB != NULL) {
            lenB++;
            curB = curB->next;
        }
        curA = headA;
        curB = headB;//恢复指针
        //求差值,为了对齐末尾
        if (lenB > lenA) {
            swap(lenA, lenB);
            swap(curA, curB);
        }
        int gap = lenA - lenB;
        
        while (gap--) {
            curA = curA->next;
        }
        //遇到curA和curB相同则返回
        while(curA != NULL) {
            if(curA == curB) {
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return curA;
        
    }
};

142. 环形链表 II

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

思路:1.判断链表是否有环2.如果有环,如何找到这个环的入口

具体:两步走:1.fast两步,slow一步,在环内相遇点相遇,相遇后进行第二步,设置index1从此处出发,设置index2从head出发,在入口点相遇,就找到了入口点。

 fast每次走两步,slow每次走1步,最后会在环内相遇,目的是求x

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

slow = x+y

fast = 2slow-->x+y +n(y+z) = 2(x+y)

n(y+z) = x+y

x = (n-1)y+nz= (n-1)(y+z)+z

当n=1,x=z说明:从头节点出发一个指针,从相遇节点也出发一个指针,这两个指针每次只走一个节点,那么当这两个指针相遇的时候就是环形入口的节点。

在相遇处,定义一个指针index1,在头节点定义index2

同时移动,相遇的地方就是入口节点

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        //两步走 fast走两步,slow走一步 在相遇点见面
        //第二部:相遇点设置index1,开头设置index2  一起走 会在入口见面
        ListNode* fast = head;
        ListNode* slow = head;
        while (fast != NULL && fast->next != NULL) {
            fast = fast->next->next;
            slow = slow->next;
            //找到相遇点,开始进行第二步
            if (fast == slow) {
                ListNode* index1 = fast;
                ListNode* index2 = head;
                while (index1 != index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;
            }
        }
        return NULL;

总结:

链表种类主要是:单链表,双链表,循环链表

链表存储方式:分散存储,通过指针连在一起

链表的增删改查

数组和链表在不同场景下的性能分析

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值