算法学习第四天|24.两两交换链表中的节点,19删除链表的倒数第N个节点|面试题02.07链表相交|142.环形链表||

目录

前言

一、两两交换链表中的节点(力扣24)

二、删除链表中的倒数第N个节点(力扣19)

三、链表相交(力扣面试题02.07)

四、环形链表II(力扣142)


前言

1.学习两两交换链表中节点的算法

2.学习删除节点中倒数第N个节点算法

3.学习如何判断两个链表是否相交

4.学习判断链表是否是循环链表

一、两两交换链表中的节点(力扣24)

实现思想:给整个链表添加一个虚拟头节点,从虚拟头节点开始每次操纵后两个点,因此每一次移动cur指针,我们都必须确保cur指针在要操作的两个节点之前

1.力扣写法

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummy_head = new ListNode(-1);
        dummy_head->next = head;
        ListNode* cur = dummy_head;
        while(cur->next != nullptr && cur->next->next != nullptr){
            ListNode* tmp = cur->next;
            ListNode* tmp1 = cur->next->next->next;

            cur->next = cur->next->next;
            cur->next->next = tmp;
            cur->next->next->next = tmp1;

            cur = cur->next->next;
        }
        return dummy_head->next;
    }
};

2.自行创建链表并实现两两交换

#include <iostream>

using namespace std;

struct ListNode{
	int val;
	ListNode* next;
	ListNode(int val):val(val), next(nullptr){}
};


//创建一个链表 
void create(ListNode* &head){
	ListNode* p = head;
	for (int i = 4; i > 0; i--){ 
		ListNode* newNode = new ListNode(i);
		newNode->next = p->next;
		p->next = newNode;
	} 
}

//遍历整个链表,并输出每一个节点的值 
void printlist(ListNode* head){
	ListNode* p = head;
	while(p){
		cout << p->val << ' ';
		p = p->next;
	}
	cout << endl;
}


void change_node(ListNode* &head){
	//给整个链表创建一个虚拟头节点 
	//虚拟头(_dummy_head)->0(head)->1->2->3->4->null 
	ListNode* _dummy_head = new ListNode(-1);
	//将虚拟头节点指向整个链表的头节点,并创建一个cur指针指向虚拟头节点
	//因为如果想交换一个节点的指针指向,那么就要找到这个节点的前一个节点 
	_dummy_head->next = head;
	//虚拟头(cur)->0(head)->1->2->3->4->null 
	ListNode* cur = _dummy_head;
	//节点的个数可能是偶数个或者奇数个,这两种情况我们都要考虑到 
	while(cur->next != nullptr && cur->next->next != nullptr){
		ListNode* tmp = cur->next;
		//虚拟头(cur)->0(head)->1(tmp)->2->3->4->null 
		ListNode* tmp1 = cur->next->next->next;
		//虚拟头(cur)->0(head)->1(tmp)->2(tmp1)->3->4->null 
		cur->next = cur->next->next;
		//虚拟头(cur)->1(tmp)->2(tmp1)->3->4->null 
		//     0(tmp) 
		cur->next->next = tmp;
		//虚拟头(cur)->1(tmp)->0(tmp)     
		//     2(tmp1)->3->4->null
		cur->next->next->next = tmp1;
		//虚拟头(cur)->1(tmp)->0(tmp)->2(tmp1)->3->4->null
		cur = cur->next->next;
		//虚拟头->1(tmp)->0(cur)->2(tmp1)->3->4->null
	}
	head = _dummy_head->next;
} 


int main(void){
	ListNode *head = new ListNode(0);
	//创建链表 
	create(head);
	//打印没有交换之前的链表 
	printlist(head);
	//两两交换链表当中的节点 
	change_node(head);
	//打印交换之后的链表 
	printlist(head);
	return 0;
} 

二、删除链表中的倒数第N个节点(力扣19)

实现思想:使用一个fast指针和一个slow指针,先让fast指针向前移动N+1个位置,因为要删除一个节点,就要找到其前一个节点,因此删除倒数第N个节点,我们要找到第N-1个节点,这样fast指针和slow指针中间形成的空间正好可以在fast指向null的时候使得slow指向第N个节点的前一个节点

1.力扣实现

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy_head = new ListNode(-1);
        dummy_head->next = head;
        ListNode* fast = dummy_head;
        ListNode* slow = dummy_head;
        while(n-- && fast != nullptr){
            fast = fast->next;
        }
        fast = fast->next;
        while(fast != nullptr && slow != nullptr){
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next;
        return dummy_head->next;
    }
};

2.自行创建链表并实现删除倒数第N个节点

#include <iostream>

using namespace std;

struct ListNode{
	int val;
	ListNode* next;
	ListNode(int val):val(val), next(nullptr){}
};


//创建一个链表 
void create(ListNode* &head){
	ListNode* p = head;
	for (int i = 4; i > 0; i--){ 
		ListNode* newNode = new ListNode(i);
		newNode->next = p->next;
		p->next = newNode;
	} 
}

//遍历整个链表,并输出每一个节点的值 
void printlist(ListNode* head){
	ListNode* p = head;
	while(p){
		cout << p->val << ' ';
		p = p->next;
	}
	cout << endl;
}


void delete_n(ListNode* &head, int n){
	//创建一个虚拟头节点指向链表的head节点 
	ListNode* _dummy_head = new ListNode(-1);
	_dummy_head->next = head;
	//创建两个指针,快指针用来在前面寻找链表尽头
	//slow指针用来指向需要删除节点的前一个节点 
	ListNode* fast = _dummy_head;
	ListNode* slow = _dummy_head;
	//先让fast指针前进 
	while(n-- && fast != nullptr){
		fast = fast->next;
	}  
	//fast指针需要多走一步才能指向需要删除节点的前一个结点 
	fast = fast->next;
	while(fast != nullptr){
		slow = slow->next;
		fast = fast->next;
	}
	//删除节点 
	slow->next = slow->next->next;
	//因为头节点也有可能被删除,所以需要重新赋值头节点 
	head = _dummy_head->next;
}


int main(void){
	ListNode *head = new ListNode(0);
	//创建链表 
	create(head);
	//打印没有删除之前的链表 
	printlist(head);
	//删除链表中倒数第N个元素 
	delete_n(head, 5);
	//打印删除之后的链表 
	printlist(head); 
	return 0;
} 

三、链表相交(力扣面试题02.07)

实现思想:这里观察链表相交的特点可以发现,两个链表相交之后的节点都是相同的,因此不论两个链表谁长谁短,将他们两个尾部对齐,其中一个指针从短的链表的第一个节点位置开始遍历,指向长的链表的指针,从短链表对应的长链表的相应位置开始遍历,如果遍历过程中指针相同就说明链表相交,否则就不相交。

1.力扣实现

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* pa = headA;
        ListNode* pb = headB;
        int lenA = 0, lenB = 0;
        while(pa!=nullptr){
            lenA++;
            pa = pa->next;   
        }
        while(pb!=nullptr){
            lenB++;
            pb = pb->next;
        }
        pa = headA;
        pb = headB;
        if (lenA < lenB){
            swap(lenA, lenB);
            swap(pa, pb);
        } 
        int gap = lenA - lenB;
        while(gap--){
            pa = pa->next;
        }
        while(pa != nullptr && pb != nullptr){
            if (pa == pb) return pa;
            pa = pa->next;
            pb = pb->next;
        }
        return NULL;
    }
};

2.自行创建相交链表实现查找链表相交

#include <iostream>
#include <vector>

using namespace std;

struct ListNode{
	int val;
	ListNode* next;
	ListNode(int val):val(val), next(nullptr){}
};


//创建链表1:   
// 4->1->8->4->5
void create_1(ListNode* &head1){
	ListNode* p = head1;
	vector<int> nums = {1, 8, 4, 5};
	for (int i = 0; i < nums.size(); i++){ 
		ListNode* newNode = new ListNode(nums[i]);
		newNode->next = p->next;
		p->next = newNode;
		p = p->next;
	} 
}

//创建链表2并连接到链表1上(两个链表相交在8处):   
//  4->1->8->4->5
//5->0->1-^ 
void create_2(ListNode* &head2, ListNode* &head1){
	ListNode* p1 = head1;
	ListNode* p2 = head2;
	vector<int> nums = {0, 1};
	for (int i = 0; i < nums.size(); i++){ 
		ListNode* newNode = new ListNode(nums[i]);
		newNode->next = p2->next;
		p2->next = newNode;
		p2 = p2->next;
	} 
	//此处的操作就是将5->0->1这段链表衔接在8上 
	p1 = p1->next;
	p2->next = p1->next;
}

//遍历整个链表,并输出每一个节点的值 
void printlist(ListNode* head){
	ListNode* p = head;
	while(p){
		cout << p->val << ' ';
		p = p->next;
	}
	cout << endl;
}

//找出两个链表相交的节点
ListNode* find(ListNode* head1, ListNode* head2){
	//定义两个指针,分别从头开始遍历两个链表 
	ListNode* p1 = head1;
	ListNode* p2 = head2;
	//记录两个链表的长度 
	int size1 = 0, size2 = 0;
	//先计算一下两个链表的长度 
	while(p1){
		p1 = p1->next;	
		size1++;
	}
	while(p2){
		p2 = p2->next;
		size2++;
	}
	//计算过后,两个指针已经指到最末尾
	//此时要将p1和p2指针在指回两个链表的头节点处 
	p1 = head1;
	p2 = head2;
	//链表1长度>链表2长度 
	if (size1 >= size2){
		//两个链表长度相差多少 
		int gap = size1 - size2;
		//因为如果两个链表有相交的地方,那么他们相交后的部分是完全相同的
		//那么要将这两个的末端对齐
		//while(gap--) p1 = p1->next;这部操作就相当于末端对齐 
		//短的链表从头部的位置开始遍历
		//长的链表在短链表头部对应位置开始遍历 
		while(gap--) p1 = p1->next;
		while(p1 != nullptr && p2 != nullptr){
			//相同则返回 
			if (p1 == p2) return p1;
			p1 = p1->next;
			p2 = p2->next;
		}
		//循环结束说明没有相同节点,返回NULL 
		return nullptr;
	}
	//与上面同理 
	else{	
		int gap = size2 - size1;
		while(gap--) p2 = p2->next;
		while(p1 != nullptr && p2 != nullptr){
			if (p1 == p2) return p2;
			p1 = p1->next;
			p2 = p2->next;
		}
		return nullptr;
	}
} 

int main(void){
	//两个链表的头节点 
	ListNode* head1 = new ListNode(4);
	ListNode* head2 = new ListNode(5);
	//创建两个链表并使他们相交 
	create_1(head1);
	create_2(head2, head1);
	//打印两个链表 
	printlist(head1);
	printlist(head2);
	//寻找两链表相交的地方 
	ListNode* result = find(head1, head2);
	cout << "这两个链表的交汇点是: "<<result->val;
	return 0;
}

四、环形链表II(力扣142)

实现思量:使用双指针算法,如果链表有环则fast指针一定会在环中追上slow指针,具体证明过程不做具体解释

1.力扣实现

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != nullptr && fast->next != nullptr){
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow){
                ListNode* index1 = fast;
                ListNode* index2 = head;
                while(index1 != index2){
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index2;
            }
        }
        return nullptr;
    
    }
};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值