DAY3! 移除链表元素,反转链表,设计链表(仍存疑)

文章目录

  • 题目:203. 移除链表元素
    • 实现算法:虚拟头节点
    • 自我实现
    • 做题心得
  • 题目:206. 反转链表
    • 实现算法:双指针
    • 自我实现
    • 做题心得
  • 题目:707. 设计链表
    • 实现算法
    • 自我实现
    • 做题心得

题目:203. 移除链表元素

链接: leetcode题目链接

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回新的头节点。

示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:
输入:head = [], val = 1
输出:[]

示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]

提示:
1).列表中的节点数目在范围 [0, 104] 内
2).1 <= Node.val <= 50
3).0 <= val <= 50

实现算法:虚拟头节点

1.虚拟头节点

如果该节点不符合条件需要删除,执行的操作一般是让上一个节点的 next 指针指向下一个节点。但问题在于,对于头节点来说,无法找到上一个节点,因此为了执行的统一性,定义一个虚拟头节点。

// 1.虚拟头节点
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        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;
    }
};

2.移动头节点指针head

除了新定义一个虚拟头节点外,也可以将头节点与后续非头节点的处理分隔开。如果非头节点不满足条件需要删除的话,依旧按照上一节点改指向下一节点操作。如果头节点不满足条件需要删除的话,就将头节点指针head向后移,使其等于 head->next 。

// 在原来链表操作
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        // 删除头结点
        while (head != NULL && head->val == val) { // 注意这里不是if
            ListNode* tmp = head;
            head = head->next;
            delete tmp;
        }

        // 删除非头结点
        ListNode* cur = head;
        while (cur != NULL && cur->next!= NULL) {
            if (cur->next->val == val) {
                ListNode* tmp = cur->next;
                cur->next = cur->next->next;
                delete tmp;
            } else {
                cur = cur->next;
            }
        }
        return head;
    }
};


p.s.
对于结构体指针,用 listnode -> val / next 访问成员。
指针定义 listnode* cur = head 。

自我实现

//更改head位置
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {

        while (head != NULL && head->val == val) {
            head = head->next;
        }
        ListNode* cur = head;
        while (cur != NULL && cur->next != NULL) {
            if (cur->next->val == val) {
                cur->next = cur->next->next;
            }
            else {
                cur = cur->next;
            }
        }
        return head;

    }
}
//虚拟头字节
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) {
                cur->next = cur->next->next;
            }
            else {
                cur = cur->next;
            }
        }
        return dummyhead->next;
    }
}

做题心得

第一次接触链表。。。可以说是非常的不熟练了,甚至还又去重新学了结构体和指针。整体来说,这道题目思路很简单。在看完题解之后也自我实现了。就是对一些定义仍有一些问题。比如设置虚拟头节点的定义。还有对边界的判断总是有问题。

题目:206. 反转链表

链接: leetcode题目链接

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

示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:
输入:head = [1,2]
输出:[2,1]

示例 3:
输入:head = []
输出:[]

提示:
1).链表中节点的数目范围是 [0, 5000]
2).-5000 <= Node.val <= 5000

实现算法:双指针

1.双指针

原链表是从前往后,因此反转之后的链表顺序就应该是从后向前。原本的头节点就变成了尾节点。因此新定义一个虚拟节点 pre 位于原头节点之前,使其为 NULL,令原头节点的 next 指针指向该 NULL 虚拟节点,就可以使其变为尾节点,再令 pre 和 cur 均向后移一位,去反转第二个节点为倒数第二节点,逐步实现新链表从后到前依次排序。

// 1.双指针法
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* temp; // 保存cur的下一个节点
        ListNode* cur = head;
        ListNode* pre = NULL;
        while(cur) {
            temp = cur->next;  // 保存一下 cur的下一个节点,因为接下来要改变cur->next
            cur->next = pre; // 翻转操作
            // 更新pre 和 cur指针
            pre = cur;
            cur = temp;
        }
        return pre;
    }
};

2.递归法

从前往后翻转指针指向。

// 递归法
class Solution {
public:
    ListNode* reverse(ListNode* pre,ListNode* cur){
        if(cur == NULL) return pre;
        ListNode* temp = cur->next;
        cur->next = pre;
        // 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
        // pre = cur;
        // cur = temp;
        return reverse(cur,temp);
    }
    ListNode* reverseList(ListNode* head) {
        // 和双指针法初始化是一样的逻辑
        // ListNode* cur = head;
        // ListNode* pre = NULL;
        return reverse(NULL, head);
    }

};

p.s.
在传递时,存在将 cur 指向 pre 后,再找不到途径获取 cur->next 了,因此需要在反转指向之前,先用一个指针记录下一个节点的地址,否则 cur 无法实现下一步的右移更新。

自我实现

//双指针
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        
        ListNode* temp,* cur,* pre;
        cur=head;
        pre=NULL;
        while(cur){
            temp=cur->next;
            cur->next=pre;
            
            pre=cur;
            cur=temp;
        }
        return pre;
    }
};

做题心得

毫不犹豫选择看题解。。。整体思路算是缕清了,但是还没有细钻研递归法。实在学不进去了。勉强完成了两道简单题,设计链表肝不动了。

题目:707. 设计链表

链接: leetcode题目链接

你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList 类:
1.MyLinkedList() 初始化 MyLinkedList 对象。
2.int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
3.void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
4.void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
5.void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
6.void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。

示例 1:
输入
[“MyLinkedList”, “addAtHead”, “addAtTail”, “addAtIndex”, “get”, “deleteAtIndex”, “get”]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]
解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2); // 链表变为 1->2->3
myLinkedList.get(1); // 返回 2
myLinkedList.deleteAtIndex(1); // 现在,链表变为 1->3
myLinkedList.get(1); // 返回 3

提示:
1).0 <= index, val <= 1000
2).请不要使用内置的 LinkedList 库。
3).调用 get、addAtHead、addAtTail、addAtIndex 和 deleteAtIndex 的次数不超过 2000 。

实现算法

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--){ // 如果--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++;
    }

    // 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
    // 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点
    // 如果index大于链表的长度,则返回空
    // 如果index小于0,则在头部插入节点
    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;
        //delete命令指示释放了tmp指针原本所指的那部分内存,
        //被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
        //如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
        //如果之后的程序不小心使用了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;

};

p.s.
定义新的结点:new Listnode(0);

自我实现



做题心得

回头再看!又没懂!!!!

你可能感兴趣的:(笔试,链表,数据结构,c++,leetcode,算法)