基础数据结构题型总结


基础数据结构题型总结

链表

力扣题库

2两数相加

c++关于链表的初始化使用不熟练,基础不扎实

逻辑没理清,卡在循环初始化节点与返回头指针的处理上,先初始化头结点后,后面就不好处理初始化新的节点,如果头结点也加入计算的话。(解决方案:直接返回头结点的下一个节点,头结点不加入计算)

优质思路:
把两个链表一长一短的情况通过三目符(?:)来使得null转换为0,使得其过程泛化,避免多条件判断下的代码冗余

基础:

ListNode() : val(0), next(nullptr) {} //初始化函数

其中:为所做的初始化,即为将0赋值给变量val,将nullptr赋值给变量next

new的作用:

1 指针变量的实体化

声明结构体时为什么不用加struct也不会报错?

指针变量和实变量的区别:

指针变量只存储指向地址,无法存储别的值,普通变量直接使用即可。

调用指针指向的变量成员需要用”->”,调用其他变量的成员使用”.”

19 删除链表的倒数第 N 个结点

个人思路:把给的倒序删除参数n转换成正序参数,然后进行删除。(链表长度+双指针法)

其他思路:

1递归实现

class Solution {
public:
    int cur=0;
    ListNode* removeNthFromEnd(ListNode* head, int n) {
       if(!head) return NULL;
       head->next = removeNthFromEnd(head->next,n);
       cur++;
       if(n==cur) return head->next;
       return head;
    }
};

2快慢指针,快指针先走n步,然后快慢一起走,直到快指针走到最后,要注意的是可能是要删除第一个节点,这个时候可以直接返回head -> next
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        if(!head | !head -> next) return NULL;
        ListNode * fast = head, *slow = head;
        for(int i = 0; i < n; i++){
            fast = fast -> next;
        }
        if(!fast){
            return head -> next;    
        }
        
        while(fast -> next){
            fast = fast -> next;
            slow = slow -> next;
        }
        slow -> next = slow -> next -> next;
        return head;
    }
};

3 栈

在遍历链表的同时将所有节点依次入栈。根据栈「先进后出」的原则,我们弹出栈的第 nn 个节点就是需要删除的节点,并且目前栈顶的节点就是待删除节点的前驱节点

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(0, head);
        stack<ListNode*> stk;
        ListNode* cur = dummy;
        while (cur) {
            stk.push(cur);
            cur = cur->next;
        }
        for (int i = 0; i < n; ++i) {
            stk.pop();
        }
        ListNode* prev = stk.top();
        prev->next = prev->next->next;
        ListNode* ans = dummy->next;
        delete dummy;
        return ans;
    }
};

21. 合并两个有序链表

小失误:没有加break,导致最后只剩下一个链表非null时while循环无法停下。

优化:
1可以只初始化一个表头,其余的拼接原有链表节点即可,不需要额外申请新节点

2最后合并时可以使用三目符减少代码量

其他思路:

递归实现:

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if (l1 == nullptr) {
            return l2;
        } else if (l2 == nullptr) {
            return l1;
        } else if (l1->val < l2->val) {
            l1->next = mergeTwoLists(l1->next, l2);
            return l1;
        } else {
            l2->next = mergeTwoLists(l1, l2->next);
            return l2;
        }
    }
};

23. 合并K个升序链表

基础缺乏:

1 结构体的定义:可以像类一样定义么?√

C++中的struct与class比较类似。struct默认访问权限是public,class是private;class有继承,多态机制,而struct没有。

2 STL优先队列的用法未掌握:参数,重载运算符的定义方式,比较函数的定义类型(为什么会重载()?)

优先队列的参数只有第一个type是必须的,后面的容器类型和比较类型会默认生成,如果要定义容器类型,那么比较类型也要定义

operator在类内定义时,参数只需要运算符后面的对象,在类外定义时(友元),参数个数按正常运算所需个数走。

优先队列如果要自定义排序函数,只能用仿函数,不能用普通函数的指针

仿函数:使一个类的使用看上去像一个函数

返回bool类型的仿函数称为谓词

如果operator()接受一个参数,那么叫做一元谓词

如果operator()接受两个参数,那么叫做二元谓词

为什么会重载():好像默认就是这样,类内重载”<”,类外重载”()”

struct tmp1 //运算符重载<
{
    int x;
    tmp1(int a) {x = a;}
    bool operator<(const tmp1& a) const
    {
        return x < a.x; //大顶堆
    }
};
struct tmp2 //重写仿函数
{
    bool operator() (tmp1 a, tmp1 b) 
    {
        return a.x < b.x; //大顶堆
    }
};
struct Status {
        int val;
        ListNode *ptr;
        bool operator < (const Status &rhs) const {
            return val > rhs.val; //小顶堆
        }
    };
struct comp {
        bool operator()(ListNode* a, ListNode* b) {
            return a->val > b->val;//小顶堆
        }
    };

3移位运算符:”>>1”当时没反应过来

4 记得声明完头结点后给头结点初始化

5 优先队列的比较函数定义还是未明白

6 链表题最重要的一点就是判断节点是否存在

24两两交换链表中的节点

未考虑交换后的末尾已交换的链表保存问题,自己误认为只需要2个指针即可,实际上需要三个,最后一个保存已交换后的链表的地址。

链表的边界判断还是不够熟练

其他思路:

递归

把整个链表看成三个部分,头结点,第二个节点,以及其他部分,交换完后返回第二个节点。边界条件是单个节点或者为空时返回

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if (head == nullptr || head->next == nullptr) {
            return head;
        }
        ListNode* newHead = head->next;
        head->next = swapPairs(newHead->next);
        newHead->next = head;
        return newHead;
    }
};

25. K 个一组翻转链表

弱点:思路没问题(使用栈结构使得数量为k的子链表进行翻转),就是实现过程代码能力不足,老是数组越界,原因是因为stack定义类型不对,应该用指针而非节点类型(差了个*),可能是因为节点存到stack中会破坏原有链表结构而指针不会?

其他思路:
尾插法:可以理解为把末尾的节点与头结点进行交换

递归:
先写出边界条件和递归,最后写出每次递归需要的相应操作并返回。

61. 旋转链表

思路:将单链表构成循环链表(头尾相连),向前遍历头结点len-(k%len)-1次断开即可(与官方题解思路一致)

这题比较简单,一次成。

86 分隔链表

思路不够严谨,想要在原有链表上做尽可能少的处理完成题目。

官方思路:弄两个头节点,一个存比目标大的节点,一个存比目标小的节点,最后再合并。

老是出现内存问题(heap-use-after-free),没有指向NULL的结点,解决方法:large那一链表末加nullptr。

92 反转链表II

个人思路:想的是先循环一遍标出头和尾指针位置,然后对这一范围内的节点使用尾插法

官方思路:思路类似,只不过尾指针的位置一直是当前节点的后一个节点,使之转移到头指针的next,

递归思路:

参考文献

[1] 引用文章标题1

[2] 引用文章标题2

[3] 引用文章标题3


Author: star
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source star !
  TOC