代码随想录算法训练营第三天 | LeetCode203.移除链表元素、LeetCode707.设计链表、LeetCode206.反转链表

LeetCode203.移除链表元素

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

思路:题目看来很简单,在链表中找到符合条件的节点进行删除即可。

有两种方式:

1、直接在原链表上进行删除,此时需要考虑头节点为需要删除的元素所在节点的情况,因此需要分情况讨论,即元素在头节点和不在头节点,分情况讨论即可,相对来说比较容易,这里不过多讨论。

2、采用虚拟节点的思想,设置一个虚拟节点,将其插入原链表的开头位置,这样不管需要删除的元素在头节点或者不在头节点,都按照不在头节点情况进行讨论,减少了分情况讨论的代价,但是需要注意的是最后返回的是虚拟节点后面的next域,而非新增加的虚拟节点。

需要注意的是,不管采用哪种方法,判断的条件比如p != NULL或者p->next != NULL要写对,多琢磨琢磨,另外对于删除的元素要及时将其delete掉,以免成为乱指的野指针,造成各种各样问题。

ListNode* removeElements(ListNode* head, int val) {
        ListNode* virtualNode = new ListNode(0, head); //创建一个虚拟节点指向原链表
        ListNode* p = virtualNode;
        while(p != NULL && p -> next != NULL){
            if(p -> next -> val == val){
                ListNode* tmp = p -> next;
                p ->next = p -> next -> next;
                delete tmp;
            }else{
                p = p -> next;
            }
        }

        head = virtualNode -> next;//将头节点指向虚拟节点的next
        delete virtualNode; //及时删除虚拟节点,节省空间
        return head;
    }    

LeetCode707.设计链表

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

单链表中的节点应该具备两个属性:valnextval 是当前节点的值,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 的节点。

思路:题目虽然看起来多,但是其实并不复杂,当找到头节点后,一切按照题目描述进行即可。这里依然采用虚拟指针的方法。

struct LinkNode{
        int val;
        LinkNode* next;
        LinkNode(){};
        LinkNode(int value): val(value){}
        LinkNode(int value, LinkNode* n): val(value), next(n){}
    };
private:
    LinkNode* dummyNode;
    int size = 0;

public:
    MyLinkedList() {
        dummyNode = new LinkNode(0, NULL); //采用虚拟节点进行初始化
    }
    
    int get(int index) {
        if(index > (size - 1) || index < 0) return -1;
        LinkNode* p = dummyNode -> next;
        while(index --){ //注意这里不能写为--index,否则会进入死循环
            p = p -> next;
        }
        return p -> val;
    }
    
    void addAtHead(int val) {
        LinkNode* newNode = new LinkNode(val);
        newNode -> next = dummyNode -> next;
        dummyNode -> next = newNode;
        size ++;
    }
    
    void addAtTail(int val) {
        LinkNode* newNode = new LinkNode(val, NULL);
        LinkNode* p = dummyNode;
        while(p != NULL && p -> next != NULL){
            p = p -> next;
        }
        p -> next = newNode;
        size ++;
    }
    
    void addAtIndex(int index, int val) {
        if(index > size) return;
        LinkNode* newNode = new LinkNode(val, NULL);
        LinkNode* p = dummyNode;
        while(index --){
            p = p -> next;
        }
        newNode -> next = p -> next;
        p -> next = newNode;
        size ++;
    }
    
    void deleteAtIndex(int index) {
        if(index > size - 1 || index < 0) return;
        LinkNode* p = dummyNode;
        while(index--){
            p = p -> next;
        }
        LinkNode* tmp = p -> next;
        p -> next = p -> next -> next;
        delete tmp;
        tmp = nullptr; //需要将删除的节点赋空值,否则会成为野指针,出现各种各样问题
        size --;
    }

LeetCode206.反转链表

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

 思路:反转链表的题目还是比较经典,主要是采用双指针来进行,其实可以成为三指针法,因为过程中还用到了tmp指针来保存cur的next节点。

主要有两种解题顺序:

1、从前往后反转,常用方法有递归法和双指针法;

2、从后往前反转,常用方法有递归法;

当然还有其他方法,比如创建虚拟节点,将原链表的结点依次链接到虚拟结点上,每次增加都是作为头节点增加,最后发挥虚拟结点的next即可;还有一种方法是使用栈,新建一个链表,将原链表元素依原顺序入栈,而后再弹出,将弹出的元素依次添加到新的链表中,每次添加都是作为末尾结点添加,这样就能得到反转后的链表,当然这时候还要注意原链表的第一个结点的next需要指向NULL,因为本身它的next是指向原链表第二个元素的,弹出后仍然指向原链表的第二个元素,所以需要修改指向。

双指针法(从前往后反转)

ListNode* reverseList(ListNode* head) {
        ListNode* cur = head;
        ListNode* pre = NULL;
        ListNode* tmp;
        while(cur != NULL){
            tmp = cur -> next; //保存后面的节点
            cur -> next = pre; //指向前面的节点
            pre = cur;
            cur = tmp; //注意交换的顺序很关键
        }
        return pre;  //此时为最后一个节点的指针,即翻转生成的链表的头节点指针
    }

 递归法(从前往后反转)

ListNode* reverse(ListNode* pre, ListNode* cur){
        if(cur == NULL) return pre;
        ListNode* tmp = cur -> next;
        cur -> next = pre;
        return reverse(cur, tmp); //思路与双指针类似,这里的递归调用相当于pre=cur;cur=tmp;
    }
ListNode* reverseList(ListNode* head) {
        return reverse(NULL, head);
    }

 递归法(从后往前反转)

ListNode* reverseList(ListNode* head) {
        //临界条件判断
        if(head == NULL) return NULL;
        if(head -> next == NULL) return head;

        //对非头结点及之后的结点进行反转
        ListNode* last = reverseList(head -> next);

        //对第一个和第二个节点进行反转
        head -> next -> next = head;
        head -> next = NULL;
        return last;

    }

希望能够帮助到你。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值