数据结构与算法 数组、链表
相关:单链表反转,链表中环的检测(快慢指针),两个有序链表的和并(类似归并排序),删除链表倒数第K个结点,寻找链表的中间结点(快慢指针)
文章目录
2135周 学习日志
数组
- 定义
- 动态扩容
- 容器与数组
- 寻址公式与随机访问特性
- 定义(3点)
- 低效的插入与删除
- 越界问题
- 不同编程语言中的数组实现方式
- 方面:一维/二维数组,一维/二维结构体数组的实现方式
- c / c + + c/c++ c/c++
- j a v a java java(二维,结构体,特殊)
- J a v a S c r i p t JavaScript JavaScript的数组(特殊)( A r r a y B u f f e r ArrayBuffer ArrayBuffer 数组与标准数组相同)
- 和并有序数组(归并排序)
- 思考: c / c + + c/c++ c/c++与 j a v a java java数组实现的优缺点(连续、空间、内存要求)
链表
-
L R U LRU LRU缓存淘汰算法
-
底层存储结构
-
定义与操作
- 查找、插入、删除(前驱结点,指定结点,值等于给定值)
-
利用哨兵化简代码
-
有头结点:便于第一个结点的插入、删除
head = new Node; head->next= nullptr;//not head=nullptr
-
线性搜索中的标记,将待搜索元素放在最后,可减少一次比较( i < n i<n i<n)
-
-
用于实现栈:可以将头结点作为栈顶,降低时间复杂度
-
循环链表,双向链表,双向循环链表
-
缓存
-
链表与数组性能对比
-
指针丢失与内存泄漏
-
指针或引用赋值的含义
-
留意边界条件与特殊情况能否正常工作
- 空链表
- 一个/两个结点
- 特殊结点:头尾
-
swap(node1, node2)理解为交换名字,
-
练习题
- 回文字符串
- 单链表反转
- 链表中环的检测(快慢指针)
- 两个有序链表的和并(类似归并排序)
- 删除链表倒数第 K K K个结点
- 寻找链表的中间结点(快慢指针)
- 双向循环链表(基本完成:11_210824.cpp)
/*单链表*/ /*头结点*/ head = new Node; head->next= nullptr;//not head=nullptr /*delete*/ while (head) { Node* p=head; head=head->next; delete p; } /*链表反转(无头结点)*/ Node* Reverse(Node* head) { Node* pre=nullptr; Node* cur=head;//有头结点 cur = head->next,head->next=nullptr; while(cur) { Node* nex=cur->next; cur->next=pre; pre=cur; cur=nex; } //有头结点 head->next = pre, return head; return pre; } /*简洁版(无头结点)*/ Node *p; for (p = nullptr; head; swap(head, p)) swap(p, head->next); return p; /*有头结点*/ Node *p; Node* cur = head->next; for (p = nullptr; cur; swap(cur, p)) swap(p, cur->next); head->next = p; return head; /*使用构造函数添加结点*/ Node *head = new Node(INT_MAX, nullptr); Node *p = head; for (int i = 0; i < 5; i++) { //关键 p->next = new Node(i, nullptr); p = p->next; } /*递归版*/ Node *ReverseList(Node *head) { if (!head || !head->next) return head; Node *newHead = ReverseList(head->next); head->next->next = head; //head is 这次函数传入的head,再没有操作之前,实际图为 head->newHead<-next<.... head->next = nullptr; return newHead; //newHead is 上一次返回的head } /*判断循环链表:快慢指针*/ bool Circle(Node *head) { if (!head || !head->next) return false; Node *slow = head; Node *fast = head->next; while (slow != fast) { if (fast == nullptr || fast->next == nullptr) return false; slow = slow->next; fast = fast->next->next; } return true; } /*和并有序链表*/ Node *MergeTwoLists(Node *list_1, Node *list_2) { Node *preHead = new ListNode; //哨兵 Node *p = preHead; while (list_1 && list_2) { if (list_1->val > list_2->val) { p->next = list_2; list_2 = list_2->next; } else { p->next = list_1; list_1 = list_1->next; } p = p->next; } p->next = list_1 ? list_1 : list_2; LitNode *tmp = preHead; preHead = preHead->next;//head delete tmp;//注意清理之前动态申请的 return preHead; } /*获取中间结点*/ Node *middleNode(Node *head) { //不知道是否会造成内存泄漏 // vector<Node*> A = {head}; // while (A.back()->next != nullptr) // A.push_back(A.back()->next); // return A[A.size() / 2]; int len = GetLength(head); ListNode *p = head; for (int i = 0; i < len / 2; i++) { p = p->next; } return p; } /*快慢指针*/ Node *middleNode(Node *head) { Node *slow = head; Node *fast = head; while (fast && fast->next) //need fast->next, avoid assess nullptr { slow = slow->next; fast = fast->next->next; } return slow; } /*删除倒数第n个结点*/ Node *RemoveNodeFromEnd(Node *head, int n) { static int cur = 0; //对于需要用来计数的变量,如果不是static 每次递归返回都是cur=0; //走至链表尾部 if (!head) return nullptr; //接下来,递归一直走到链表尾部 head->next = RemoveNodeFromEnd(head->next, n); //递归开始返回,cur为倒数第几个结点 cur++; if (n == cur) { ListNode *p = head; head = head->next; delete p; return head; // return head->next; } //return head->next;//仅这样会内存泄漏 return head; } int GetLength(Node *head) { int length = 0; Node *p = head; while (p) { length++; p = p->next; } return length; } Node *RemoveNodeFromEnd(Node *head, int n) { int length = GetLength(head); //存在问题 如果length=n,对于头节点特别对待 Node *p = head; if (length == n) { head = head->next; delete p; return head; } for (int i = 0; i < length - n - 1; i++) //length-n-1 is preNode { p = p->next; } Node *q = p->next; p->next = q->next; delete q; return head; } Node *RemoveNodeFromEnd(Node *head, int n) { int length = GetLength(head); ListNode *guard = new ListNode(0, head); ListNode *pre = guard; for (int i = 0; i < length - n; i++) //length-n is preNode { pre = pre->next; } Node *q = pre->next; pre->next = q->next; delete q; Node *result = guard->next; //head may be changed/deleted , cant return head delete guard; return result; } Node *RemoveNodeFromEnd(ListNode *head, int n) { Node *guard = new ListNode(0, head); ListNode *p = guard; stack<ListNode *> list; while (p) { list.push(p); p = p->next; } for (int i = 0; i < n; i++) { list.pop(); } p = list.top(); Node *q = p->next; p->next = q->next; delete q; q = guard; guard = guard->next; delete q; return guard; } Node *RemoveNodeFromEnd(Node *head, int n) { Node *guard = new ListNode(0, head); Node *fast = guard; Node *slow = guard; for (int i = 0; i < n + 1; i++) //need add 1,slow为前驱结点 { fast = fast->next; } while (fast) { fast = fast->next; slow = slow->next; } Node *p = slow->next; slow->next = p->next; delete p; p = guard; guard = guard->next; return guard; } Node *RemoveNodeFromEnd(Node *head, int n) { Node *guard = new ListNode(0, head); Node *fast = guard; Node *slow = guard; for (int i = 0; i < n + 1; i++) //need add 1,slow为前驱结点 { fast = fast->next; } while (fast) { fast = fast->next; slow = slow->next; } Node *p = slow->next; slow->next = p->next; delete p; p = guard; guard = guard->next; return guard; }