记录算法基础题思路:
链表逆序:三指针法,创建一个新链表new_head, 遍历旧链表,1) tmp记录node的next,避免丢失 2)node的next指向new_head 3)更新new_head位置到node 4)node通过tmp更新下一个
反转从位置 m 到 n 的链表:关键记录4个位置,left,right,left前一个,right后一个, 后面两个用于串联逆序后的链表;
链表求交点:计算A,B长度差值,先把长的移动到两边一样长,再一起移动找相同节点地址;
环形链表:快慢指针,fast每次比slow多走一步,只要再fast走到结尾之前fast追上slow就存在环;
链表划分:通过两个新链表一个存大于x,一个小于等于x,再把链表连接起来,利用哑节点简化节点转移
复杂链表复制:两轮遍历,第一次遍历创建节点,并建立map<node, index>, 第二次遍历串联节点,并把random指针通过map填写完成;
链表合并:递归加分治,把链表数量拆分,最小到2个单元的时候进行merge,merge类似通过哑节点简化;
step1:
链表逆序:https://leetcode-cn.com/problems/reverse-linked-list/
反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL思路:head往前遍历一遍链表,遍历过程生成head_new指向的逆向链表,节点调整next指向head_new过程会修改指针 必须要先备份节点的next;
class Solution {
public:
ListNode* reverseList(ListNode* head) {
/* 三指针法 */
ListNode *new_head = NULL;
while (head != NULL) {
ListNode *tmp = head->next; // 先标记next,避免修改next后找不到后继
head->next = new_head; // 把当前node指向new_head链
new_head = head; // 更新new_head到新元素;
head = tmp; // head指向下一个
}
return new_head;
}
};
反转从位置 m 到 n 的链表:https://leetcode-cn.com/problems/reverse-linked-list-ii/
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明: 1 ≤ m ≤ n ≤ 链表长度。思路:从m、n进行逆序,需要关注的4个位置:1、m的前驱节点(链接 逆序完成后的 头节点) 2、m节点(开始逆序) 3、n节点 4、n的后置节点(逆序后连接尾部节点)。
需要思考特殊场景:如果m==1,从第一个节点开始逆序,应该要返回最后一个反转的节点(逆序后的头结点),否则返回默认的初始头节点;
ListNode* reverseBetween(ListNode* head, int m, int n) {
ListNode *cur = head;
ListNode *reserve_start_pre = NULL;
int cnt = n - m + 1;//反转次数
bool start_from_head = m == 1 ? true : false;
if (n == m && m == 1) {
return head;
}
while (m > 1 && cur != NULL) {
reserve_start_pre = cur; /* 1 记录反转前pre节点 要链接反转尾节点 */
cur = cur->next;
m--;
}
ListNode *reserve_start_node = cur; /* 2 记录反转的第一个节点 要连 反转尾节点后一个节点 */
ListNode *pre = reserve_start_pre;
while (cnt > 0 && cur != NULL) { /* 3 进行节点反转,反转结束后反转尾节点尾pre,下一个节点尾cur */
ListNode *next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
cnt--;
}
reserve_start_node->next = cur; /* 连接反转尾后一个节点 */
if (start_from_head == true) {
return pre; // 如果从首节点开始,最后返回值应该是,最后一个反转的点
} else {
reserve_start_pre->next = pre; /* 连反转后尾部节点 */
return head;
}
}
链表求交点:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
编写一个程序,找到两个单链表相交的起始节点。
思路:假如两个链表有交点,那么A和B链表尾部是链接的,可以从更短的链表位置开始遍历,找到两个相同的位置。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
/* 计算A,B长度差值,先把长的移动到两边一样长,再一起移动 */
// 计算差值,先移动较长的链表
int lengthA = 0;
int lengthB = 0;
ListNode *tmp = headA;
while (tmp != NULL) {
lengthA++;
tmp = tmp->next;
}
tmp = headB;
while (tmp != NULL) {
lengthB++;
tmp = tmp->next;
}
while (lengthA != lengthB) {
if (lengthA > lengthB) {
lengthA--;
headA = headA->next;
} else {
lengthB--;
headB = headB->next;
}
}
// 两个相同长度的链表同时往后走,相同为交点
while (headA != NULL && headB != NULL) {
if (headA == headB) {
return headA;
}
headA = headA->next;
headB = headB->next;
}
return NULL;
}
};
链表求环:https://leetcode-cn.com/problems/linked-list-cycle/
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。思路:利用快慢指针,slow指针进入环后,fast指针每次移动多靠近一格slow,因此如果有环一定会在一圈内追到slow。
如果要求meet的交点也可以按下图公式求,第一次meet后 到焦点距离为c,而从开始的位置到交点具体a=c+n个整圈,可以在meet后搞一个指针从a开始跑,第一次相交为meet;
class Solution {
public:
bool hasCycle(ListNode *head) {
/* 快慢指针,fast每次比slow多走一步,只要再fast走到结尾之前fast追上slow就存在环; */
ListNode *fast = head;
ListNode *slow = head;
while (fast != NULL) {
slow = slow->next;
fast = fast->next;
if (fast == NULL) {
break; /* 走到尾 说明没有环 */
}
fast = fast->next; /* fast多走一步 */
if (fast == slow) {
return true;
}
}
return false;
}
};}
链表划分:https://leetcode-cn.com/problems/partition-list/
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
示例:
输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5、思路:设置两个临时哑节点,在遍历链表过程,根据值大小插入对应的哑节点链表中,最后把两个链表链接起来(注意绕过哑巴节点);
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
/* 通过两个新链表一个存大于x,一个小于等于x,再把链表连接起来,利用哑节点简化节点转移 */
ListNode less(0); // 哑节点简化节点转移
ListNode more(0);
ListNode *less_list = &less;
ListNode *more_list = &more;
while (head != NULL) {
if (head->val < x) {
// 小节点放到less链表
less_list->next = head;
less_list = head;
} else {
// 大节点放到more链表
more_list->next = head;;
more_list = head;
}
head = head->next;
}
less_list->next = more.next; // 串联大小链表跳过哑节点
more_list->next = NULL; // 最后尾部加NULL
return less.next; // 跳过哑节点返回less起始节点
}
};
复杂链表复制:https://leetcode-cn.com/problems/copy-list-with-random-pointer/
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
要求返回这个链表的 深拷贝。思路:做两次遍历,第一次遍历建立一个原链表的拷贝链表,在拷贝的同时将新老链表的节点地址映射到哈希表。
处理拷贝链表的random节点,通过上一步的哈希表找到原链表random节点指向的节点在拷贝链表中对应的节点地址。
Node* copyRandomList(Node* head) {
map<Node*, int> pos_map; /* 记录旧链表 的地址 和 pos的对应关系 */
vector<Node*> node_vector; /* 新链表 ,通过数组 体现 节点位置 和 地址 对应关系 */
Node * ptr = head;
int i = 0;
/* 第一轮编译 创建新节点数组 的 同时生成旧链表 地址-pos关系的map */
while (ptr != NULL) {
node_vector.push_back(new Node(ptr->val));
pos_map[ptr] = i;
ptr = ptr->next;
i++;
}
node_vector.push_back(NULL); /* 插入一个NULL元素,这样后续链表连接后最后一个指向空 */
ptr = head;
i = 0;
/* 第二轮遍历 连接新链表 的 同时 通过map表 填写新的链表random指针 */
while (ptr != NULL) {
node_vector[i]->next = node_vector[i + 1];
if (ptr->random != NULL) {
node_vector[i]->random =
node_vector[pos_map[ptr->random]];
}
ptr = ptr->next;
i++;
}
return node_vector[0];
}
链表合并:https://leetcode-cn.com/problems/merge-k-sorted-lists/
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度
思路:利用分治+递归,把链表进行递归拆分到2组中,一直到2个链表然后进行合并,最终形成一个完整的链表。
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode tmp_head(0);
ListNode *head = &tmp_head;
while (l1 != NULL && l2 != NULL) {
if (l1->val > l2->val) {
head->next = l2;
head = head->next;
l2 = l2->next;
} else {
head->next = l1;
head = head->next;
l1 = l1->next;
}
}
while (l1 != NULL) {
head->next = l1;
head = head->next;
l1 = l1->next;
}
while (l2 != NULL) {
head->next = l2;
head = head->next;
l2 = l2->next;
}
return tmp_head.next;
}
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if (lists.size() == 0) {
return NULL;
}
if (lists.size() == 1) {
return lists[0];
}
if (lists.size() == 2) {
return mergeTwoLists(lists[0], lists[1]);
}
int mid = lists.size() / 2;
vector<ListNode *> list_a;
vector<ListNode *> list_b;
for (int i = 0; i < mid; i++) {
list_a.push_back(lists[i]);
}
for (int i = mid; i < lists.size(); i++) {
list_b.push_back(lists[i]);
}
ListNode *l1 = mergeKLists(list_a);
ListNode *l2 = mergeKLists(list_b);
return mergeTwoLists(l1, l2);
}
};