代码随想录算法训练营第四天| 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、160.链表相交、142.环形链表II

24两两交换链表中的节点

 题目链接/文章讲解/视频讲解: 代码随想录

1.代码展现

	//24 两两交换链表中的节点
	ListNode* swapPairs(ListNode* head) {
		//创建虚拟头节点
		ListNode* dummyHead = new ListNode();
		dummyHead->next = head;
		ListNode* cur = dummyHead;
		//注意顺序
		while (cur->next != nullptr && cur->next->next != nullptr) {
			//交换前储存丢失节点,这样就不会丢失了
			ListNode* temp = cur->next;
			ListNode* temp1 = cur->next->next->next;
			//开始交换,分三步走
			cur->next = cur->next->next;
			//注意:不是cur->next->next->next,这个时候已经更新了
			cur->next->next = temp;
			temp->next = temp1;
			//交换完毕,更新cur
			cur = cur->next->next;
		}
		return dummyHead->next;
	}

 2.本题小节
        设计到处理头节点的算法,用虚拟头节点会简单很多,因此首先要创建虚拟头节点;明确交换步骤,共有三步,画图分析,同时注意交换时要保存临时节点,否则会出现找不到需要指向的节点;明确每次遍历的节点cur的位置,为将要处理的节点对的前一个,因此每次循环为cur=cur->next->next,初始为虚拟头节点;明确遍历结束的条件,当节点个数为偶数时,cur->next = nullptr时结束遍历,当节点个数为奇数时,cur->next->next = nullptr时结束遍历,返回值为虚拟头节点后的节点。

19.删除链表的倒数第N个节点  

题目链接/文章讲解/视频讲解:代码随想录

1.代码展现

//19 删除链表倒数第n个节点 
	ListNode* removeNthFromEnd(ListNode* head, int n) {
		//构建虚拟头节点
		ListNode* dummyHead = new ListNode();
		dummyHead->next = head;
		//创建快慢指针
		ListNode* nodeSlow = dummyHead;
		ListNode* nodeFast = dummyHead;
		//for (int i = 0; i <= n; i++) {
		//	nodeFast = nodeFast->next;
		//}
		//?注意nodeFast不能为nullptr
		while (n-- && nodeFast != nullptr) {
			nodeFast = nodeFast->next;
		}
		nodeFast = nodeFast->next;
		//开始移动快慢指针,直到快指针指向为空
		//注意不是nodeFast->next
		while (nodeFast != nullptr) {
			nodeSlow = nodeSlow->next;
			nodeFast = nodeFast->next;
		}
		//当快指针指向为空时,满指针此时的位置为要移除元素的前一位
		//修改指针指向,并释放内存
		ListNode* temp = nodeSlow->next;
		nodeSlow->next = nodeSlow->next->next;
		delete temp;
		temp = nullptr;
		//返回头节点
		return dummyHead->next;
	}

 2.本题小节
        本题使用的是双指针的解法,使用双指针的目的是寻找到倒数第n个节点的位置。因为会处理到头节点,因此还是需要构建虚拟头指针;构建快慢指针,初始值都为虚拟头指针,然后将快指针往后移动n+1次,这样是为了保证快指针指向nullptr时,慢指针指向寻找的节点的前一处,保证删除能正常进行。明白了这些,这题就很好做了。

160.链表相交

题目链接/文章讲解:代码随想录

1.代码展现

//160 链表相交
ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
		//step1:统计两个链表的长度,并计算长度差
		int nSizeA = 0;
		int nSizeB = 0;
		ListNode* curA = headA;
		ListNode* curB = headB;
		//注意:这里不可以用head遍历
		while (curA != nullptr) {
			nSizeA++;
			curA = curA->next;
		}
		while (curB != nullptr) {
			nSizeB++;
			curB = curB->next;
		}
		cout << "sizeA:" << nSizeA << endl;
		cout << "sizeB:" << nSizeB << endl;
		//计算长度差
		//重新指向头部
		curA = headA;
		curB = headB;
		//这里swap的应用避免了分情况讨论
		if (nSizeB > nSizeA) {
			swap(headA, headB);
			swap(curA, curB);
		}
		int nLength = abs(nSizeA - nSizeB);
		
		//step2: 将A链表的curA往左移动nLength位
		while (nLength--) {
			curA = curA->next;
		}

		//step3: 从curA和curB处开始遍历,检查curA是否等于curB,相同则返回curA或curB,为相交的起始节点
		while (curA != nullptr) {
			//注意,这里是ListNode相同,而不是val相同
			if (curA == curB) {
				return curA;
			}
			curA = curA->next;
			curB = curB->next;
		}
		//没遍历到,则返回NULL
		return NULL;

	}

2.本题小节

        求两个链表长度之差n;将长的链表的cur往后移动n次,此时两个链表遍历次数相同;同时对两个链表进行遍历获取,如果存在curA=curB,那么该节点则为相交的初始节点。

142.环形链表II

题目链接/文章讲解/视频讲解:代码随想录

1.代码展现 

//142 环形列表
	ListNode* detectCycle(ListNode* head) {
		//本题用快慢指针来求解
		ListNode* fastNode = head;
		ListNode* slowNode = head;
		
		//step1: 判断是否有环,这里的fastNode->next != nullptr是为了保证
		//fastNode->next->next有效,否则会报错
		while (fastNode != nullptr && fastNode->next != nullptr) {
			//step2: 移动指针,慢指针每次移动一个节点,快指针次移动两个节点
			fastNode = fastNode->next->next;
			slowNode = slowNode->next;
			if (fastNode == slowNode) {
				//此时相遇了
				//step3:记下这个相遇点,由数学公式2(x+y)= x+y+n(y+z)
				// 推导出x = (n - 1)(z+y) + z
				//其中x为头节点到入环节点的距离,n表示fast节点转圈圈数,y表示入环节点到相遇点的距离
				// z表示相遇点到入环节点的距离,
				// 可知
				//从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 
				//那么当这两个指针相遇的时候就是 环形入口的节点。
				ListNode* curA = head;
				ListNode* curB = fastNode;
				while (curA != curB) {
					curA = curA->next;
					curB = curB->next;
				}
				//此时找到环形入口节点
				return curA;
			}

		}
		//没有环
		return NULL;
	}

 2.本题小节
        本题的重点在于两处,一处是如何判断有环,通过快慢指针判断,快指针追上慢指针后就可以判断有环;一处是如何计算环入口的位置,这里设计到数学推导,将数学公式推导一遍后,可以知道,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。

链表总结

总结:代码随想录 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值