代码算法训练营day3 | 203移除链表元素、707设计链表、206反转链表

4 篇文章 0 订阅

203移除链表元素

题目链接
状态:
文档:programmercarl.com

思路:
有两种办法,一种是不用虚拟头节点,要分两种情况讨论。(删除头节点和非头节点)
一种是用虚拟头节点,不用分情况讨论。
很显然第二种比较方便,并且虚拟头节点也是经常使用的,所以选择用这种方法。

虚拟头节点,顾名思义,就是代替了原有的头节点,所以dummyhead -> next = head;
因为要遍历链表中的每一个元素,来判断是否和要寻找的目标元素的值相等,可以选择一直使用head->next ->value== val;来进行判断,但这样的话head就不能参与判断了,所以又会走入第一种办法的漩涡中。所以这里要添加一个指针cur,指向dummyhead,通过不断的cur->next->value ==val;来进行判断。

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        //使用虚拟头节点
        ListNode* dummyhead = new ListNode(0); //创建虚拟节点
        dummyhead->next = head; //让虚拟节点变成虚拟头节点
        ListNode* cur = dummyhead; //设置一个临时的遍历变量,指向虚拟头节点

        //开始遍历
        while(cur!=NULL && cur->next!=NULL)
        {
            //如果匹配成功了
            if(cur->next->val == val)
            {
                ListNode* tmp = cur->next; //保存一下这个节点
                //改变目标值前一个值的next的值
                cur->next = cur->next->next;
                delete tmp; //释放目标值
            }
            else{
                cur = cur->next;
            }
        }
        //全部遍历完,结果处理
        head = dummyhead->next;
        delete dummyhead;
        return head;
    }
};

注意: 不要忘记最后删除掉虚拟头节点。


707设计链表

题目链接
状态:
文档:programmercarl.com

实现过程中的问题:
①在实现//将一个值插入到下标为index的节点之前 void addAtIndex(int index, int val)函数的时候,忘记了分类讨论:如果index大于链表的长度,则返回空。如果index小于0,则在头部插入节点。
②在实现//删除链表中下标为index的节点 void deleteAtIndex(int index)函数的时候,忘记将创建出来的临时变量用完后置为空,delete命令指示释放了tmp指针原本所指的那部分内存,被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针,如果之后的程序不小心使用了tmp,会指向难以预想的内存空间。

代码:

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

    MyLinkedList() {
        // 设置一个虚拟头节点
        dummy_head = new LinkedNode(0);
        size = 0;
    }

    // 获取下标为index的节点的值,下标无效返回-1
    int get(int index) {
        if (index < 0 || index > (size - 1))
            return -1;
        //dummy_head->next = head; //设定虚拟头节点
        // 因为要遍历元素,所以设置一个临时变量cur,指向头节点
        LinkedNode* cur = dummy_head->next; //cur指向头节点
        while (index--) { //index==0 时跳出while循环
            cur = cur->next;
        }
        return cur->val;
    }

    //将一个值插入到链表中第一个元素之前
    void addAtHead(int val) {
        //变成头节点
        LinkedNode* newNode = new LinkedNode(val);
        newNode->next = dummy_head->next;
        dummy_head->next = newNode;
        size++;
    }

    //将一个值追加到最后一个元素之后
    void addAtTail(int val) {
        //变成最后一个节点
        LinkedNode* newNode = new LinkedNode(val);
        LinkedNode* cur = dummy_head;
        while(cur->next!=NULL) //cur的下一个还有数的时候
        {
            cur = cur->next;
        }
        //此时cur指向最后一个数
        cur->next = newNode;
        size++;
    }

    //将一个值插入到下标为index的节点之前
    void addAtIndex(int index, int val) {
        //也就是插入到index的位置上
        if(index > size) return;
        if(index < 0) index = 0;  

        LinkedNode* newNode = new LinkedNode(val);
        LinkedNode* cur = dummy_head; //遍历指针
        while(index--)
        {
            cur = cur->next;
        }
        newNode->next = cur->next;

        cur->next = newNode;
        size++;
    }

    //删除链表中下标为index的节点
    void deleteAtIndex(int index) {
        if(index >= size || index <0) return;
        LinkedNode* cur = dummy_head;
        //开始找下标为index的节点 即 cur->next指向的节点
        while(index--)
        {
            cur = cur->next;
        }
        //创建新的节点存放被删除的那个节点
        LinkedNode* tmp = cur->next;
        cur->next = cur->next->next;
        size--;
        delete tmp;
        //delete命令指示释放了tmp指针原本所指的那部分内存,
        //被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
        //如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
        //如果之后的程序不小心使用了tmp,会指向难以预想的内存空间
        tmp = NULL;
    }

private:
    int size;               // 链表长度
    LinkedNode* dummy_head; // 虚拟头节点
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */

206反转链表

题目链接
状态:
文档:programmercarl.com

思路:
过程图解
把指针的箭头调个头,就很容易了。代码中实现的是第二行。
因为反转后,原来的节点就有新的next指向的值了,所以要新建一个节点pre去存放每个节点反转后的新的next。
又因为原来节点的next指向改变了,所以需要一个临时指针tmp来存放这个节点原来存放的next的节点,不然到后面节点会丢失。

实现过程中的问题:
①在while循环中,

 tmp = cur->next; //存放节点原来的next
 cur->next = pre; //改变指向

//我写把左值和右值写反了,经常会出现这种错误!!!
//错误写法:
cur->next = tmp;
pre = cur->next;

②还是在while循环中,

pre = cur;
cur = tmp;

//两个赋值顺序写错了,
//错误写法:
cur = tmp;
pre = cur;
//很明显就不对,应该在cur被赋值之前就先赋值给pre才行,大意了。

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        //只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表
        ListNode* cur = head;
        ListNode* pre = NULL; //反转后的头节点
        ListNode* tmp; //用于存放cur->next
        while(cur)
        {
            tmp = cur->next; //存放节点原来的next
            cur->next = pre; //改变指向
            //pre cur都进行后移
            pre = cur;
            cur = tmp;
        }
        //注意pre现在才是我们的头节点,不能再返回head了
        return pre;
    }
};

总结:
链表问题很有意思,指针的指向也非常灵活,也要记得保存可能会丢失的那个值,很常见的就是cur->next。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值