链表相关算法学习
链表基础
指针元素引用 head->val
对象元素引用 head.val
struct ListNode {
int val;
ListNode *next;
};
int main(){
ListNode a;
ListNode b;
a.val = 1;
a.next = &b;
ListNode *head = &a;
while(head){
cout << head->val << endl;
head = head->next;
}
}
链表逆序
整体逆序
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *new_head = NULL;
while(head){
ListNode* next = head->next;
head->next = new_head;
new_head = head;
head = next;
}
return new_head;
}
};
部分逆序
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
int change_len = n - m + 1; //需要逆序的个数
ListNode *pre_head = NULL;
ListNode *result = head;
while(head && --m){ //找到逆序开始点位置head以及前一个位置pre_head
pre_head = head;
head = head->next;
}
ListNode *modify_list_tail = head; //记录逆序后尾节点位置
ListNode *new_head = NULL; //new_head比head落后一个位置
while(head && change_len){
ListNode *next = head->next;
head->next = new_head;
new_head = head;
head = next;
change_len --;
}
modify_list_tail->next = head;
if (pre_head){
pre_head->next = new_head;
}
else{
result = new_head;
}
return result;
}
};
链表求交点
思路1:使用set求交点
遍历链表A,将A中节点指针地址插入set
遍历B,查看B中节点指针地址是否出现在set中
class Solution {
public:
ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
set<ListNode*> node_set;
while(headA){
node_set.insert(headA);
headA = headA->next;
}
while(headB){
if(node_set.find(headB) != node_set.end())
return headB;
headB = headB->next;
}
return NULL;
}
};
思路2:获取A、B长度,由差加双指针得知交点位置
遍历链表A、B,获取长度
遍历B n-m次
双指针同时移动,对比指针所指节点地址
class Solution {
public:
ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
int list_A_len= get_list_length(headA);
int list_B_len= get_list_length(headB);
if(list_A_len > list_B_len){
headA = forward_long_list(list_A_len, list_B_len, headA);
}
else{
headB = forward_long_list(list_B_len, list_A_len, headB);
}
while(headA && headB){
if(headA == headB)
return headA;
headA = headA->next;
headB = headB->next;
}
return NULL;
}
int get_list_length(ListNode* head){
int len = 0;
while(head){
len ++;
head = head->next;
}
return len;
}
ListNode* forward_long_list(int long_len, int short_len, ListNode* head){
int delta = long_len - short_len;
while(head && delta){
head = head->next;
delta --;
}
return head;
}
};
链表求环
思路1:使用set求交点
遍历链表A,如果节点地址不存在,则将当前节点地址加入set集合
class Solution {
public:
ListNode* getIntersectionNode(ListNode* head) {
set<ListNode*> node_set;
while(head){
if(node_set.find(head) != node_set.end())
return head;
node_set.insert(head);
headB = headB->next;
}
return NULL;
}
};
思路2:快慢指针
快指针一次走两步,慢指针一次走一步,当相遇时,当前节点位置与交点距离 = 头节点与交点距离
头---------交点----------相遇的地方
| |
| |
| |
---------------------
记头到交点距离为a,交点到相遇地方为b,相遇地方到交点距离为b或c
慢指针从头走到相遇的地方,行进距离:a+b;快指针行进距离:2(a+b)
则有2(a+b)=(a+b)+ c + b ==> a=c
class Solution {
public:
ListNode* getIntersectionNode(ListNode* head) {
ListNode* fast = head;
ListNode* slow = head;
ListNode* meet = NULL;
while(fast){
slow = slow->next;
fast = fast->next;
if (!fast){
return NULL;
}
fast = fast->next;
if (fast == slow){
meet = fast;
break;
}
}
if (meet == NULL){
return NULL;
}
while(head && meet){
if (head == meet){
return head;
}
head = head->next;
meet = meet->next;
}
return NULL;
}
};
链表划分
题目说明
已知链表头指针head与数值x,将所有小于x的节点放在大于或等于x的节点前,且保持原有相对位置
思路1:巧用临时头节点
双指针:一个记录小于x的节点地址;一个记录大于x的节点地址,最后将两者相连
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode less_head(0);
ListNode more_head(0);
ListNode* less_ptr = &less_head;
ListNode* more_ptr = &more_head;
while(head){
if(head->val < x){
less_ptr->next = head;
less_ptr = head;
}
else{
more_ptr->next = head;
more_ptr = head;
}
head = head->next;
}
less_ptr->next = more_head.next;
more_ptr->next = NULL;
return less_head.next;
}
};
复杂链表复制
题目说明
已知一个复杂的链表,节点中有一个指向本链表任意某个节点的随机指针,求链表的深度拷贝
struct RandomListNode {
int label;
ListNode *next, *random;
RandomListNode(int x) : label(x), next(NULL), random(NULL) {}
};
思路1:使用map将节点地址与节点序号对应起来
class Solution {
public:
RandomListNode* copyRandomList(RandomListNode* head) {
map<RandomListNode *, int> node_map;
vector<RandomListNode *> node_vec;
RandomListNode *ptr = head;
int i = 0;
while(ptr){
node_vec.push_back(new RandomListNode(ptr->label));
node_map[ptr] = i;
ptr = ptr->next;
i ++;
}
node_vec.push_back(0);
ptr = head;
i = 0;
while(ptr){
node_vec[i]->next = node_vec[i+1];
if (ptr->random){
int id = node_map[ptr->random];
node_vec[i]->random = node_vec[id];
}
ptr = ptr->next;
i ++;
}
return node_vec[0];
}
};
2个排序链表归并
题目说明
已知两个已排序链表,将其合并,并保证合并后依然有序
思路1:双指针
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode temp_head(0);
ListNode *pre = &temp_head;
while(l1 && l2){
if (l1->val < l2->val){
pre->next = l1;
l1 = l1->next;
}
else{
pre->next = l2;
l2 = l2->next;
}
pre = pre->next;
}
if (l1){
pre->next = l1;
}
if (l2){
pre->next = l2;
}
return temp_head.next;
}
};
K个排序链表归并
思路1:暴力合并 递归合并 k个链表需k-1次
思路2:排序后相连
class Solution {
public:
bool cmp(const ListNode *a, const ListNode *b){
return a->val < b->val;
}
ListNode* mergeKLists(vector<ListNode* >& lists) {
vector<ListNode *> node_vec;
for (int i = 0; i < lists.size(); i++){
ListNode* head = lists[i];
while(head){
node_vec.push_back(head);
head = head->next;
}
}
if(node_vec.size() == 0)
return NULL;
sort(node_vec.begin(), node_vec.end(), cmp);
for (int i = 1; i < node_vec.size(); i++){
node_vec[i-1]->next = node_vec[i];
}
node_vec[node_vec,size()-1]->next = NULL;
return node_vec[0];
}
};
思路3:分治 将k分为若干组 分别排序
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* >& sub1_lists;
vector<ListNode* >& sub2_lists;
for (int i = 0; i < mid; i++){
sub1_lists.push_back(Lists[i]);
}
for (int i = mid; i < lists.size(); i++){
sub2_lists.push_back(Lists[i]);
}
ListNode* l1 = mergeKLists(sub1_lists);
ListNode* l2 = mergeKLists(sub2_lists);
return mergeTwoLists(l1, l2);
}
};