欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > 数据结构之链表算法题

数据结构之链表算法题

2024/12/21 23:54:54 来源:https://blog.csdn.net/2403_87718362/article/details/144427265  浏览:    关键词:数据结构之链表算法题

一:移除链表元素

我们很容易就可以想到一个解决方案:再创建一个链表,把不是val的结点拿过来尾插。

这样确实可以但是,我们每次尾插都需要遍历一遍整个链表,这样时间复杂度就变成了O(n^2),

因此我们不妨设置一个tail尾结点来指向它的尾。

画图分析

这里面还有一个潜在的问题就是,假如最后一个节点的数值==val怎么办,想一想。

完整代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {ListNode* newhead;ListNode* newtail;ListNode* pcur = head;newhead=newtail=NULL;while(pcur){if(pcur->val!=val){if(newhead==NULL){newhead=newtail=pcur;}else{newtail->next=pcur;newtail=pcur;}}pcur=pcur->next;}if(newtail!=NULL&&newtail->next!=NULL){newtail->next=NULL;}return newhead;
}

二:反转链表

这一题有两种解法:

解法一:

直接反转指向,比如原来二指向三,现在让三指向2.

画图分析:

经过分析我们写出了这样的代码:

struct ListNode* reverseList(struct ListNode* head) {

    struct ListNode* pprev = NULL;

    struct ListNode* pcur = head;

    struct ListNode* pnext = head->next;

    while(pcur)

    {

        pcur->next=pprev;

        pprev=pcur;

        pcur=pnext;

        pnext=pnext->next;

    }

    return pprev;

}

放在力扣上提交

遇到错误不要慌张,我们看一下提示信息,可以发现,原来是对NULL解引用了,所以在17行那要加上判断,那么说到这了万一这个链表原来就是空呢?因此在一开始也要对这种情况处理。

完整代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
struct ListNode* reverseList(struct ListNode* head) {struct ListNode* pprev = NULL;struct ListNode* pcur = head;struct ListNode* pnext;if(head){pnext = head->next;}while(pcur){pcur->next=pprev;pprev=pcur;pcur=pnext;if(pnext){pnext=pnext->next;}}return pprev;
}

解法二:

取原链表中元素,头插到新链表的newhead中。

画图分析:

完整代码:

struct ListNode* reverseList(struct ListNode* head) {struct ListNode* pcur = head;struct ListNode* newhead = NULL;struct ListNode* pnext;if(head){pnext=head->next;}while(pcur){pcur->next = newhead;newhead = pcur;pcur = pnext;if(pnext){pnext = pnext->next;}}return newhead;
}

三:链表的倒数k个结点

我们确实可以先遍历一遍拿到链表长度,然后就可以找到倒数k个结点了。但是如果只能这么做就没有拿出来的必要了qwq。

快慢指针法:两个指针先让一个走k,然后一起走,等到k指向空(具体看下面画图分析)结束。

画图分析:

完整代码:

struct ListNode* FindKthToTail(struct ListNode* pHead, int k ) {struct ListNode* slow;struct ListNode* fast;slow=fast=pHead;while(k--){if(fast){fast=fast->next;}else {return NULL;}}while(fast){slow=slow->next;fast=fast->next;}return slow;
}

四:链表的中间结点

这题同样也是快慢指针法,大体思路是让fast走两步,slow走一步,但是直觉告诉我们结点数的奇偶性会影响结束条件。

画图分析:

完整代码:

struct ListNode* middleNode(struct ListNode* head) {struct ListNode* slow;struct ListNode* fast;fast=slow=head;while(fast&&fast->next){slow=slow->next;fast=fast->next->next;}return slow;
}

五:合并两个有序链表

思路:依次比较链表中的结点,然后插入小的结点

画图分析:

完整代码:

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {struct ListNode* newhead;struct ListNode* newtail;newhead=newtail=NULL;struct ListNode* pcur1=list1;struct ListNode* pcur2=list2;if(pcur1==NULL)return pcur2;if(pcur2==NULL)return pcur1;while(pcur1&&pcur2){if(pcur1->val<pcur2->val){if(newhead==NULL){   newhead=newtail=pcur1;}else{newtail->next=pcur1;newtail=pcur1;}pcur1=pcur1->next;}else{if(newhead==NULL){   newhead=newtail=pcur2;}else{newtail->next=pcur2;newtail=pcur2;}pcur2=pcur2->next;}}if(pcur1!=NULL){newtail->next=pcur1;}if(pcur2!=NULL){newtail->next=pcur2;}return newhead;
}


优化:

我们每次都要对新创建的头结点判断是否为空。

有没有一种方法可以不用判断呢?:设置一个哨兵位就可以了

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {struct ListNode* pcur1=list1;struct ListNode* pcur2=list2;if(pcur1==NULL)return pcur2;if(pcur2==NULL)return pcur1;struct ListNode* head = (struct ListNode*)malloc(sizeof(struct ListNode));struct ListNode* tail = head;while(pcur1&&pcur2){if(pcur1->val<pcur2->val){tail->next=pcur1;tail=pcur1;pcur1=pcur1->next;}else{tail->next=pcur2;tail=pcur2;pcur2=pcur2->next;}}if(pcur1!=NULL){tail->next=pcur1;}else{tail->next=pcur2;}struct ListNode* newhead=head->next;free(head);return newhead;
}

六:链表分割

这题没给图,直接画图分析:

画图分析:

完整代码:

class Partition {
public:ListNode* partition(ListNode* pHead, int x) {ListNode* lesshead =(ListNode*)malloc(sizeof(ListNode));ListNode* lesstail=lesshead;ListNode* greaterhead=(ListNode*)malloc(sizeof(ListNode));ListNode* greatertail=greaterhead;ListNode* pcur=pHead;if(pcur==NULL)return NULL;while(pcur){if(pcur->val<x){lesstail->next=pcur;lesstail=pcur;}else {greatertail->next=pcur;greatertail=pcur;}pcur=pcur->next;}lesstail->next=greaterhead->next;greatertail->next=NULL;ListNode* newhead=lesshead->next;free(lesshead);free(greaterhead);return newhead;}
};

七:链表回文

首先这一题开一个900的数组也可以过,但是他并不符合空间复杂度O(1)。

然后我们说一下这一题的思路:先找中间结点,然后逆置,最后一一比较。

具体细节,请看下

画图分析:

完整代码:

/*
struct ListNode {int val;struct ListNode *next;ListNode(int x) : val(x), next(NULL) {}
};*/
struct ListNode* middleNode(ListNode* A)
{ListNode* slow = A;ListNode* fast = A;while(fast && fast->next){fast=fast->next->next;slow=slow->next;}return slow;
}
struct ListNode* reverseList(struct ListNode* head)
{struct ListNode* pprev = NULL;struct ListNode* pcur = head;struct ListNode* pnext;if(head){pnext = head->next;}while(pcur){pcur->next = pprev;pprev = pcur;pcur = pnext;if(pnext){pnext = pnext->next;}}return pprev;
}
class PalindromeList {
public:bool chkPalindrome(ListNode* A) {ListNode* mid = middleNode(A);ListNode* rhead = reverseList(mid);ListNode* curA = A;ListNode* curR = rhead;while(curA && curR){if(curA->val != curR->val){return false;   }else{curA=curA->next;curR=curR->next;}}return true;}
};

八:链表相交

思路:遍历一遍求长度(同时判断是否会相交),长的走差值步,然后一起走直到两指针相同

这一题不可以用逆置做,想一想为什么??

画图分析:

完整代码:


struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {struct ListNode* curA = headA;struct ListNode* curB = headB;int lenA = 1; int lenB = 1;while(curA->next){lenA++;curA=curA->next;}while(curB->next){lenB++;curB=curB->next;}if(curA!=curB)return NULL;int gap = abs(lenA-lenB);struct ListNode* longlist = headA;struct ListNode* shortlist = headB;if(lenA < lenB){longlist = headB;shortlist = headA;}while(gap--){longlist=longlist->next;}while(longlist!=shortlist){longlist=longlist->next;shortlist=shortlist->next;}return longlist;
}

九:环形链表1

思路:快慢指针法,slow走1步fast走2步。如果相遇则带环,否则不带环

画图分析:

完整代码:

bool hasCycle(struct ListNode *head) {struct ListNode* slow = head;struct ListNode* fast = head;while(fast && fast->next){fast=fast->next->next;slow=slow->next;if(fast==slow)return true;}return false;
}

十:环形链表2

这一题有点数学题的意思。还是先说思路:还是先slow与fast走,等到两指针相遇,在再让一个指针从都走,另一个指针从相遇位置走,直到二者再次相遇即为如环点。

画图分析:

完整代码:

struct ListNode *detectCycle(struct ListNode *head) {struct ListNode* slow = head;struct ListNode* fast = head;while(fast && fast->next){fast=fast->next->next;slow=slow->next;if(slow==fast){struct ListNode* cur = head;struct ListNode* curC = fast;while(cur!=curC){cur=cur->next;curC=curC->next;}return cur;}}return NULL;
}

十一:复杂随机链表的拷贝

这一次最难搞的是random指针。思路也不好想,大家还是借鉴一下大佬的思路吧。

思路:在每个节点后面插入一个一样的节点,定义cur 和 next指针 cur->next->radom=cur->radom->next,然后迭代往后。最后解开插入的结点。

画图分析:

完整代码:


struct Node* copyRandomList(struct Node* head) {struct Node* cur = head;//插入新节点while(cur){struct Node* newnode = (struct Node* )malloc(sizeof(struct Node));newnode->val = cur->val;newnode->next = cur->next;cur->next = newnode;cur = newnode->next;}//处理randomcur = head;while(cur){struct Node* newnode = cur -> next;if(cur->random==NULL){newnode->random = NULL;}else{newnode->random = cur->random->next;}cur = newnode->next;}//取新节点尾插,同时恢复原链表cur = head;struct Node* newhead = NULL;struct Node* newtail = NULL;while(cur){struct Node* newnode = cur->next;struct Node* next = newnode->next;if(newhead==NULL){newhead = newtail = cur->next;}else{newtail->next = newnode;newtail = newnode;}cur->next = newnode->next;cur = newnode->next;}return newhead;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com