前言:
最近在复习数据结构方面的知识,总结知识, 同时也温故而知新!
本章介绍链表往年面试常考题集一 (共6道题)。 希望能够帮助到您!
一、移除链表元素 – leetcode
描述:
删除链表中等于给定值val的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
分析:
这道题思路很简单, 我们采用双指针法。 一个指针指向删除的结点A, 另一个指针指向被删除结点的前一个结点B。 然后结点B指向结点A的下一个结点, 删除结点A。 更新结点A、结点B。
源码:
ListNode* removeElements(ListNode* head, int val) {
ListNode* cur = head;
ListNode* prev = nullptr;
while(cur) {
if(cur->val == val) {
//找到了 - 删除 - 连接
if(!prev) {
//删除的是头节点 - 更新头结点信息
ListNode* tmp = cur->next; //保存下一个结点
head = tmp; //头节点偏移
delete cur; //释放该结点
cur = tmp; //更新cur
}
else {
//删除非头节点
ListNode* tmp = cur->next; //保存下一个结点
prev->next = tmp; //连接
delete cur; //释放该结点
cur = tmp; //更新
}
}
else {
prev = cur; //prev后移
cur = cur->next; //cur后移
}
}
return head;
测试结果:
二、反转链表 – leetcode
描述:
反转一个链表
示例:
输入:1->2->3->4->5->NULL
输出:5->4->3->2->1->NULL
分析:
反转一个链表, 我们使用三指针法, prev、cur、next三个指针, 这三个指针分别连续指向三个节点。 cur指针指向prev, 这样就反转了一个结点。 然后更新prev、cur、next三个结点信息, 以此类推!
源码:
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr;
ListNode* cur = head;
ListNode* next = head;
while(cur) {
next = cur->next; //更新next
cur->next = prev; //更改指向
prev = cur; //更新prev
cur = next; //更新cur
}
head = prev; //更新head指向
return head;
}
测试结果:
三、链表的中间结点 – leetcode
描述:
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例1:
输入:[1,2,3,4,5]
输出: 3
示例2:
输入:[1,2,3,4,5,6]
输出:4
分析:
这道题是有规律的, 我们定义两个指针slow、fast,一开始都指向head。 slow指针一次偏移1步, fast指针一次偏移2步。 当fast走到头时, 此时slow指向的结点就是中间结点。
当fast->next结点不为空时, 还需要偏移。(个人设计不一样!)
源码:
ListNode* middleNode(ListNode* head) {
//头节点为空
if(head == nullptr) {
return nullptr;
}
ListNode* slow = head;
ListNode* fast = head;
while(fast->next) {
fast = fast->next->next; //fast偏移
slow = slow->next; //slow偏移
if(!fast) { //fast指针为空
break;
}
}
return slow;
}
测试结果:
四、链表中倒数第K个结点 – 牛客网
描述:
输入一个链表,输出该链表中倒数第k个结点。
分析:
我们可以定义两个快慢指针fast、slow, 让fast一开始偏移K步, 然后一起偏移, 最终slow指向的结点就是我们想要的结点。(当然也可以让fast一开始偏移k-1步, 个人设计不同!)
如果一开fast偏移k-1步, 最后只不过是循环fast判断条件不同罢了!
源码:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(!pListNode) {
return nullptr;
}
ListNode* slow = pListHead;
ListNode* fast = pListHead;
for(int i = 0; i < k; ++i) {
if(!fast) { //如果fast为空, 证明该链表没有K这么长。
return nullptr; //则这道题没有意义。返回nullptr
}
fast = fast->next;
}
while(fast) {
fast = fast->next; //fast偏移
slow = slow->next; //slow偏移
}
return slow;
}
测试结果:
五、合并两个有序链表 – leetcode
描述:
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例1:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
分析:
在数据结构教科书中链表就有这道题, 这道题也不难, 理清思路很关键!
我们先从这两个链表中找起始值最小的作为我们最终连接的主链表。(这样做的好处不需要开辟空间,缺点就是原来的这两条链表不能再用了, 因为是在这两个链表上做的。)
我画了最繁琐的, 好需要考虑一个链表走完了,另一个链表没有走弯,好需要链接没有走完的。
源码:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(l1 == nullptr) { //l1链表为空,直接返回l2
return l2;
}
else if(l2 == nullptr) { //l2链表为空,直接返回l1
return l1;
}
ListNode* head = nullptr; //head为连接后合并链表头节点
ListNode* tmp1 = l1;
ListNode* tmp2 = l2;
if(l1->val < l2->val) { //选取起始结点最小值链表
head = l1;
tmp1 = tmp1->next;
}
else {
head = l2;
tmp2 = tmp2->next;
}
ListNode* tmp = head; //head结点的偏移结点。-- 连接节点
while(tmp1 && tmp2) {
if(tmp1->val <= tmp2->val) { //l1->val < l2->val
//连接tmp1
tmp->next = tmp1; //连接
tmp = tmp1; //更新tmp
tmp1 = tmp1->next; //tmp1指向下一个结点
}
else {
//连接tmp2
tmp->next = tmp2; //连接
tmp = tmp2; //更新tmp
tmp2 = tmp2->next; //tmp2指向下一个结点
}
}
if(!tmp1) { //l1走完了
tmp->next = tmp2; //连接l2剩余结点
}
else if(!tmp2) { //l2走完了
tmp->next = tmp1; //连接l1剩余结点
}
return head; //返回合并链表头结点head
}
测试结果:
六、链表分割 – 牛客网
描述:
编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前。
给定一个链表的头指针 ListNode* pHead,请返回重新排列后的链表的头指针。注意:分割以后保持原来的数据顺序不变。
分析:
我们的思路是创建两个链表头结点L1,L2, L1链表连接的是小于x的结点。 L2链表连接的是大于等于x的结点。 最后将L1、L2链表合并。
源码:
ListNode* partition(ListNode* pHead, int x)
{
// write code here
if(!pHead){
return nullptr;
}
//LF == L1, RF == L2
//LB为LF的连接指针, LB为头指针
//RB为RF的连接指针, RB为头指针
ListNode *LF, *LB, *RF, *RB;
LF = nullptr;
RF = nullptr;
LB = LF;
RB = RF;
while(pHead) {
if(pHead->val < x) { //LF链表添加一个
//连接在LF
if(!LF) { //一开始LF为空。
LF = pHead;
LB = LF;
}
else {
LB->next = pHead; //连接
LB = LB->next; //偏移
}
}
else {
//连接在RF
if(!RF) {
RF = pHead; //一开始RF为空
RB = RF;
}
else {
RB->next = pHead; //连接
RB = RB->next; //偏移
}
}
pHead = pHead->next; //偏移
}
if(!LF) { //LF为空,代表没有小于x的值
RB->next = nullptr;
return RF; //返回RF
}
if(!RF) { //RF为空,代表没有大于等于x的值
LB->next = nullptr;
return LF; //返回LF
}
LB->next = RF; //连接LF和RF两个链表
RB->next = nullptr; //RF链表最后结点置为nullptr
return LF; //返回LF
}
测试结果:
本章到此结果, 本人可能组织语言不太好, 有些知识真的是会,但是讲不出, 有种只会意会不可言传的感觉。 请见谅!
如果有误, 欢迎指导!!!