最近在某站发现的一门质量挺高的课程,以下为课程笔记!
基本操作
节点结构
typedef struct{
int val;
Node* next;
}Node;
查找操作
Node* search(int val){
Node* pNode = root; // 根结点
while(pNode != nullptr)
if(pNode->val == val){
return pNode;
}
pNode = pNode->next;
}
return nullptr;
}
删除操作–给出要删除结点的前一个结点
void delNode(Node* prev){
Node* curr = prev->next;
prev->next = curr->next;
delete curr;
}
删除操作–给出要删除的结点
void delNode(Node* curr){
if(curr->next != nullptr){
Node* temp = curr->next;
curr->val = temp->val;
curr->next = temp->next;
delete temp;
}else{
Node* pNode = root; // 头节点
while(pNode->next != curr){
pNode = pNode->next;
}
pNode->next = nullptr;
delete curr;
}
}
相关算法
链表元素去重
Remove Duplicates from Sorted List
Given a sorted linked list, delete all duplicates such that each element appear only once.For example, Given 1->1->2,return 1->2.Given 1->1->2->3->3, return 1->2->3
Node* deleteDuplicates(Node* head){
if(head == nullptr){
return nullptr;
}
Node* node = head;
while(node->next != nullptr){
if(node->val == node->next->val){
Node* temp = node->next;
node->next = temp->next;
delete temp;
}else{
node = node->next;
}
}
return head;
}
Dummy Node技巧
考虑:
a. 哪个节点的next指针会受到影响,则需要修正该指针
b.如果待删除节点是动态开辟的内存空间,则需要释放这不部分内存(c/c++)
利用dummy node是一个非常好用的trick:只要涉及操作head节点,当头节点操作不确定的时候,不妨创建dummy node:
ListNode dummy = new ListNode(0);
dummy->next = head;*
.
.
Remove Duplicates from Sorted List ||
Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.
For example,
Given 1->2->3->3->4->4->5,return 1->2->5.
Given 1->1->1->2->3, return 2->3
Node* deleteDuplicates(Node* head){
if(head == nullptr){
return nullptr;
}
Node* dummy = new Node(0);
dummy->next = head;
Node* node = dummy;
while(node->next != nullptr && node->next->next != nullptr){
if(node->next->val == node->next->next->val){
int val_pre = node->next->val;
while(node->next != nullptr && val_pre == node->next->val){
Node* temp = node->next;
node->next = temp->next;
delete temp;
}
}else{
node = node->next;
}
}
return dummy->next;
}
给定一个链表和一个值,写一个reorder重排这个链表,使比值小的全在值左边,比值大的全在值右边
Node* partition(Node* head, int x){
if(head == nullptr){
return nullptr;
}
Node* dummyleft = new Node(0);
Node* dummyright = new Node(0);
Node* left = dummyleft;
Node* right = dummyright;
Node* node = head;
while(node->next != nullptr){
if(node->val < x){
left->next = node;
left = left->next;
}else{
right->next = node;
right = right->next;
}
node = node->next;
}
left->next = dummyright->next;
right->next = nullptr;
return dummyleft->next;
}
追赶指针技巧
对于寻找list某个特定位置的问题,不妨用两个chaser和runner, 以不同的速度遍历list,找到目标位置: Node chaser = head,runner = head. 并且可以用一个简单的小test case来验证
寻找链表的中点
Node* midpoint(Node* head){
if(head == nullptr){
return nullptr;
}
Node* chaser = head, runner = head;
while(runner && runner->next){
runner = runner->next->next;
chaser = chaser->next;
}
return chaser;
}
寻找链表倒数第k个结点
Node* findkthtoLast(Node* head, int k){
if(head == nullptr || k < 0){
return nullptr;
}
Node* runner = head;
Node* chaser = head;
while(k){
runner = runner->next;
}
if(runner == nullptr){
return nullptr;
}
while(runner->next != nullptr){
runner = runner->next;
chaser = chaser->next;
}
return chaser;
}
给定一个有环的链表,寻找环开始的结点
分析:寻找某个特定位置,用runner technique 。Runner以两倍速度遍历,假定有loop,那么runner与chaser一定能在某点相遇。相遇后,再让chaser从head触发再次追赶runner,第二次相遇的结点为loop开始的位置
如何判断两个单链表是否有交点
将链表第k个元素之后的结点移到最前面
Example:
list = 10->20->30->40->50->60 k = 4
change to 50->60->10->20->30->40
void rotate(Node** root, int k){
if(k == 0){
return;
}
Node* current = root;
for(int count = 1; count < k && current != nullptr;count++){
current = current->next;
}
if(current == nullptr){
return;
}
Node *kthNode = current;
while(current->next != nullptr){
current = current->next;
}
current->next = root;
root = kthNode->next;
kthNode->next = nullptr;
}
.
模式识别
1 . 在遍历linked list时,注意每次循环只处理一个或一对节点。核心的节点只处理当前这一个,否则很容易出现重复处理的问题。
.
反转链表
Reverse Linked List
Reverse the linked list and return the new head.
—递归版本—
Node* reverseList(Node* head){
if(head == nullptr){
return nullptr;
}
Node* prev = nullptr;
while(head != nullptr){
Node* curr = head;
head = head->next;
curr->next = prev;
prev = curr;
}
return prev;
}
—非递归版本—
Node* reverseList(Node* head){
if(head == nullptr){
return head;
}
if(head->next == nullptr){
return head;
}
Node* newHead = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return newhead;
}
.
模式识别
2. Swap Node问题
3. 交换两个节点,不存在删除的话,两个节点的prev节点的next指针,以及这两个节点的next指针,会受到影响。总是可以
a. 先交换两个prev节点的next指针的值
b. 再交换这两个节点的next指针的值
无论这两个节点的相对位置和绝对位置如何,以上的处理方式总是成立
.
交换两个相邻的节点
Swap Adjacent Nodes
Given a linked list,swap every two adjacent nodes and return its head.
Node* swapPairs(Node* head){
if(head == nullptr){
return head;
}
Node* dummy = new Node(0);
dummy->next = head;
Node* prev = dummy;
Node* node1 = head;
Node* node2 = head->next;
while(node1 && node1->next != nullptr){
node2 = node1->next;
prev->next = node1->next;
node1->next = node2->next;
node2->next = node1;
prev = node1;
node1 = prev->next;
}
return dummy->next;
}
.
模式识别
3. 同时处理两个linked list的问题,循环的条件一般可以用while(l1 && l2), 再处理剩下非NULL的list这样的话,边界情况特殊处理,常规情况常规处理。
.
链表相加
Add List Sum
Given two linked lists, each element of the lists is a integer. Write a function to return a new list, whick is the “sum” of the given two lists.
Part a. Given input (7->1->6) + (5->9->2), output 2->1->9.
Part b. Given input (6->1->7) + (2->9->5), output 9->1->2.
Part a–
Node* addTwoNumbers(Node* l1, Node* l2){
Node dummy(0);
Node* p = &dummy;
int cn = 0;
while(l1 || l2){
int val = cn + (l1 ? l1->val : 0) + (l2 ? l2->val : 0);
cn = val / 10;
val = val % 10;
p->next = new Node(val);
p = p->next;
if(l1){
l1 = l1->next;
}
if(l2){
l2 = l2->next;
}
}
if(cn != 0){
p->next = new Node(cn);
p = p->next;
}
return dummy->next;
}
Partb–
解题分析:对于a,靠前节点的解不依赖靠后节点,因此顺序遍历求解即可。对于b,靠前节点的解依赖于靠后节点(进位),因此必须用递归或栈处理。并且,subproblem返回的结果,可以是一个自定义的结构(进位 + sub-list)。当然,也可以reverse List之后再用a的解法求解。
合并两个或多个排序的链表
Merge Two Sorted List
Merge two sorted linked lists and return it as a new list.
Node* mergeTwoLists(Node* l1, Node* l2){
Node* dummy = new Node(0);
Node* curr = dummy;
while(l1 && l2){
if(l1->val <= l2->val){
curr->next = l1;
l1 = l1->next;
}{
curr->next = l2;
l2 = l2->next;
}
curr = curr->next;
}
curr->next = (l1 != nullptr) ? l1 : l2;
return dummy->next;
}
Merge K Sorted List
Node* mergeKLists(vector<Node*>&lists){
if(lists.size() == 0)return nullptr;
Node* p = lists[0];
for(int i = 0; i < l1sts.size(); i++){
p = mergeTwoLists(p, lists[i]);
}
return p;
}
未完!