算法学习day3

算法学习day3

  • 1.链表理论基础
  • 2. 力扣203题-移除链表元素
    • 2.1 不设置虚拟头节点
    • 2.2 设置虚拟头节点
  • 3. 力扣707题-设计链表
  • 4.力扣206题-反转链表
    • 4.1 双指针法
    • 4.2 递归法
  • 5.参考资料

1.链表理论基础

链表是一种常用的数据结构,它是由一系列节点构成的。每个节点都包含一个数据元素和一个指向下一个节点的指针。链表的实现方式很多,例如单向链表、双向链表等等。
在c++中,可以通过定义一个结构体或类来实现链表。一个基本的单链表节点定义如下。

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

注:结构体后面有一个分号不要忘记了
算法学习day3_第1张图片
图片来源:代码随想录

2. 力扣203题-移除链表元素

2.1 不设置虚拟头节点

分析:注意删除头节点与删除非头节点的操作不同。具体代码如下。

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        //方法1:不设置虚拟头节点
        //当要删除的就是头节点的时候
        //head !=NULL ,防止出现空指针的错误
        //head ->val == val 找到说明头节点要删除
        while( head != NULL && head->val == val){
            ListNode * temp = head;//释放内存的操作
            head = head->next;//节点移动完毕
            delete temp;
        }
        //当要删除的不是头节点的时候
        //先设置一个临时指针用来遍历,如果用head的话会改变,最后输出有问题的
        ListNode * cur = head;
        //cur 防一手空指针问题
        //cur->next !=NULL 是因为要用到cur->next->val 与val对比,如果为空。那么又是操作空指针
        while( cur != NULL && cur->next != NULL){
            if(cur->next->val == val){
                ListNode * temp = cur->next;
                cur->next = cur->next->next;
                delete temp;
            }else{
                cur = cur->next;
            }
        }
        return head;
    }
};

2.2 设置虚拟头节点

分析:设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了。

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        //方法2:设置虚拟头节点
        ListNode* dummyHead = new ListNode(0);//设置一个虚拟头节点
        dummyHead->next = head;//将虚拟头节点指向head.为后面的删除操作提供方便
        ListNode* cur = dummyHead;
        //由于后面要用到cur->next->val 于val对比,所以while循环的条件是cur->next != NULL
        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;        
    }
};

3. 力扣707题-设计链表

分析:使用虚拟头节点,慢慢做。

class MyLinkedList {
public:
    //定义结构体
    struct LinkedNode{
        int val;
        LinkedNode* next;
        LinkedNode(int val):val(val),next(nullptr){};
    };

    //初始化链表
    MyLinkedList() {
        _size = 0;
        _dummyHead = new LinkedNode(0);//定义一个虚拟头节点

    }
    //获取到第index个节点数值,若index非法数值返回-1。注意index是从0开始的,第0个节点就是头节点
    int get(int index) {
    //_size是链表的长度,_size - 1即为链表最后一个节点的下标。
        if(index < 0 || index > (_size - 1)){ return -1; }//index非法情况
        //定义一个指针cur
        //若用head,会改变head。那么无法返回链表的头节点了
        LinkedNode* cur = _dummyHead->next;
        while(index--){
            cur=cur->next;
        }
         //直接考虑极端情况,当index为0的时候,此时不进入循环直接返回第一个值
        return cur->val;
    }
    //在链表最前面插入一个节点,插入完成后,新插入的节点为链表的头结点。
    void addAtHead(int val) {
        LinkedNode* newNode = new LinkedNode(val);
        LinkedNode* cur = _dummyHead;
        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=0,那么新插入的节点为链表的新头节点
    //如果index等于链表的长度,则说明是新插入的节点为链表的尾节点
    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 < 0 || index > (_size - 1)){return;}
        LinkedNode* cur = _dummyHead;
        while(index--){
            cur=cur->next;
        }
        LinkedNode* temp = cur->next;
        cur->next = cur->next->next;
        delete temp;
        _size--;
    }
private:
    int _size;
    LinkedNode* _dummyHead;
};

4.力扣206题-反转链表

算法学习day3_第2张图片
图片来源:代码随想录

4.1 双指针法

分析:定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。首先将cur->next 节点用tmp指针保存一下。然后改变 cur->next 的指向,将cur->next 指向pre 然后继续移动pre和cur指针。
最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        //1.双指针法
        ListNode* temp;//保存cur的下一个节点
        ListNode* cur = head;
        ListNode* per = NULL;
        while(cur){
            temp = cur->next;//保存操作
            cur->next = per;//翻转操作
            //跟新per与cur指针
            per = cur;
            cur = temp;
        }
        return per;
    }
};

4.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);
    }
};

5.参考资料

代码随想录:https://www.programmercarl.com

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