4)C++的new(刷链表和二叉树前看)、链表总结

众所周知,C语言有一个非常让人抓狂的玩意那就是指针。

先前我们创造数组的时候,一直使用了vector来代替数组,有了迭代器也不需要指针了,可以说总算在数组层面把这烦人的玩意给抛之脑后了。

但是现在马上要刷链表和二叉树了,它来了它来了,我们最讨厌的指针它又回来了。

(链表和二叉树机考大概率是不会考的,面试有概率会面)

为啥链表和二叉树需要用指针

得益于链表和二叉树在C++中的独特结构(next,left和right指向的是地址),一旦设计到地址相关了,那么指针就必须得出现了。

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {

    }
};

对于这么一个函数,传进来的是一个链表的指针,而使用->就可以直接访问指针结构体里面的值,非常方便(所以推荐大家使用本地IDE编辑器,因为在leetcode上,你输入一个“.”的时候,它不会自动帮你变成->!)

head->val;
head->next;

怎样创建一个链表节点或者二叉树节点

方法1:虽然题目用的是指针,但我就要自己选择不用指针,使用值

// 这里的2和3是初始给节点赋值,不重要
ListNode node1(2);
ListNode node2(3);
node1.next = &node2;

这种写法其实并不是很推荐,因为一会是用值的,一会是用指针的,格式看起来会非常混乱,所以还是推荐统一使用指针。

ListNode *node1;

但创建指针的时候我们又遇到了一个问题,这玩意毕竟只是一个指针,声明完了之后也只是一个野指针,并没有实体的内存让我们指过去。你要是先创造一个值然后再用指针指过去当我没说。

这时候就会想到C语言之中有一个叫malloc的玩意,是用来给指针申请地址的。对malloc没印象也没关系,不影响。

现在我们需要给创造好的指针一个内存,在C++中,我们可以使用new来申请一个地址,使用例如下:

ListNode *node1 = new ListNode(2);
ListNode *node2 = new ListNode(3);
node1->next = node2;

释放内存?

如果学过C语言会知道,再malloc完一个堆区地址之后,需要使用free函数将这个地址给释放。同理,在C++的new中有一个类似的,使用完new之后也需要使用delete来释放内存,不然这个内存就会被永久占用。

但是。

你现在是在刷题,只要能实现功能就行,释放不释放内存不重要,所以请无视“使用new申请完内存必须使用delete释放掉”这条规矩。

链表题附加

好了,现在你可以快乐去刷链表中的题目了。

为了防止代码随想录中链表的题目不够,我在这边附上一些:

876. 链表的中间结点 - 力扣(LeetCode)

21. 合并两个有序链表 - 力扣(LeetCode)

148. 排序链表 - 力扣(LeetCode)

143. 重排链表 - 力扣(LeetCode)

61. 旋转链表 - 力扣(LeetCode)

23. 合并 K 个升序链表 - 力扣(LeetCode)

82. 删除排序链表中的重复元素 II - 力扣(LeetCode)

83. 删除排序链表中的重复元素 - 力扣(LeetCode)

234. 回文链表 - 力扣(LeetCode)

328. 奇偶链表 - 力扣(LeetCode)

25. K 个一组翻转链表 - 力扣(LeetCode)

链表总结

链表题的核心难点在于:

1.做着做着可能会搞蒙这个next是啥,下一个next是啥,谁指向了我我又指向了谁

2.做着做着就把某一个节点给丢了再也找不到它了,访问一个next就莫名其妙变成了空指针然后报错

而链表题也使用了很多双指针法,双指针法的主要用处是:

1.找中点,设置快慢指针,快指针每次移动两位,慢指针每次移动一位

2.找倒数第n个点,快指针先移动n次,然后和慢指针一起移动,等快快指针移动到nullptr了说明慢指针也移动到倒数第n个点了。

链表题鸡贼做法

之前我个人认为链表在机考中大概率不会考,是因为链表题有个很鸡贼的做法,这个鸡贼法能做大部分链表题(除了环形链表、相交链表之类的)

那就是拿一个vector<ListNode*>数组去把链表的所有节点给存起来,然后当数组做。

这样就直接解决了“做着做着就把某一个节点给丢了再也找不到它了”这个问题。

vector<ListNode *> vec;
for (ListNode *ptr = head; ptr; ptr = ptr->next) {
    vec.push_back(ptr);
}

比如说我要做206翻转链表。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (!head) return nullptr;
        vector<ListNode *> nodes;
        for (ListNode *ptr = head; ptr != nullptr; ptr = ptr->next) {
            nodes.push_back(ptr);
        }
        nodes[0]->next = nullptr;
        for (int i = 1; i < nodes.size(); i++) {
            nodes[i]->next = nodes[i - 1];
        }
        return nodes.back();
    }
};

再比如说我要做148排序链表,正常做法需要先完成:有序链表合并、然后归并排序。

但是鸡贼一点就自己定义一个sort把它给完成了。

class Solution {
public:
    class cmp{
    public:
        bool operator()(ListNode *a, ListNode *b) {
            return a->val < b->val;
        }
    };
    ListNode* sortList(ListNode* head) {
        if (!head) return nullptr;
        vector<ListNode*> nodes;
        for (ListNode *ptr = head; ptr != nullptr; ptr = ptr->next) {
            nodes.push_back(ptr);
        }
        sort(nodes.begin(), nodes.end(), cmp());
        for (int i = 1; i < nodes.size(); i++) {
            nodes[i - 1]->next = nodes[i];
        }
        nodes[nodes.size() - 1]->next = nullptr;
        return nodes[0];
    }
};

当然,这里并不提倡大家使用这种方法来做链表的题目,因为这样就违背了链表题的初衷

  • 29
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值