判断回文链表(剑指offer.027)
目錄
? -數(shù)組法-
? -遞歸法-
?-快慢指針-
-題目-
/*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/bool isPalindrome(struct ListNode* head) {}(注意:這題的條件為不帶頭結(jié)點的單鏈表,head就是鏈表的第一個結(jié)點)
(這里函數(shù)返回類型bool使用方法是直接返回false或者true。)
????????看到題目,博主第一想法就是頭插法再建立一個鏈表,遍歷兩個鏈表就能得出該鏈表是否是回文的。但這個思路實現(xiàn)起來容易出錯,以至于我改了n遍都沒改對= =。
下面記錄三個力扣官方給的三個思路(圖片來源也是力扣官方)
-數(shù)組法-
將鏈表的值依次放進數(shù)組,再判斷數(shù)組里的數(shù)是不是回文序列。
bool isPalindrome(struct ListNode* head){struct ListNode *p;int a[100000], i = 0;p = head;while(p){a[i++] = p -> val;p = p -> next;}int l = 0, r = i - 1;while(1){if(l >= r){break;}if(a[l] != a [r]){return false;}l ++;r --;}return true; }-遞歸法-
基本思路是創(chuàng)建兩個指針:frontNode從前面開始遍歷鏈表,currentNode從后面開始遍歷鏈表。這個方法的思路不難理解,復(fù)雜的是currentNode通過遞歸調(diào)用函數(shù)來從鏈表尾開始往前遍歷。
struct ListNode* frontNode;bool recursivelyCheck(struct ListNode* currentNode) {if (currentNode != NULL) {if (!recursivelyCheck(currentNode -> next)) {return false;}if (currentNode -> val != frontNode -> val) {return false;}frontNode = frontNode -> next;}return true; }bool isPalindrome(struct ListNode* head) {frontNode = head;return recursivelyCheck(head); }這里介紹一下currentNode實現(xiàn)其功能的過程
? ? ? ??這個過程涉及到棧,但是不用學(xué)到棧也能看懂,只需要知道棧是后進先出就行。
? ? ? ? 假設(shè)該鏈表存有五個數(shù)據(jù)5-6-1-7-5(不為回文序列),從主函數(shù)調(diào)用recursivelyCheck(head)開始,這一步將currentNode指向#0(這里用'#數(shù)字'來表示第幾個結(jié)點)。currentNode不為null所以執(zhí)行if,計算機確定了currentNode的下一個結(jié)點為#1,便將這個結(jié)點的值傳入遞歸函數(shù)。(在調(diào)用函數(shù)前,計算機會將這個信息在棧中記錄他在哪里)接下來再調(diào)用函數(shù)recursivelyCheck(#1)將currenyNode指向#1,同樣的currentNode不為null所以執(zhí)行if,計算機確定了currentNode的下一個結(jié)點為#2,再將其傳入遞歸函數(shù),再在棧中記錄。再調(diào)用函數(shù)...
-信息在棧中存儲的模式如下圖-
?
(從recursivelyCheck(head)這一步開始入棧)
? ? ? ? 遞歸調(diào)用進行到recursivelyCheck(#4)時,currentNode仍不為null所以執(zhí)行if,再下一步時遞歸進行到recursivelyCheck(NULL),因為鏈表里共有五個數(shù),#4?-> next就是空指針NULL。此時if不執(zhí)行,直接返回true給上一級。
? ? ? ? 這一級currentNode指向#4,!true為假故不返回flase,執(zhí)行currentNode指向#4為5,frontPointer指向#0為5,兩者相等故不返回false,然后frontPointer后移一個結(jié)點,來到最后一行代碼,返回true給上一級。棧頂?shù)男畔⒁瞥?(如圖,原本最頂端的信息移除)。
? ? ? ? 這一級currentNode指向#3,同樣不返回false,執(zhí)行currentNode指向#3為7,frontPointer指向#1為6,兩者不相等故在這一步直接返回false給上一級。(可以知道若每一次currentNode和frontPointer都相等的話最后會返回true給原函數(shù)。)
? ? ? ? 這一級currentNode指向#2,?!false為真故直接返回false給上一級。從這以后就不用考慮兩個指針的值了,因為false會被一層層返回給原函數(shù)。
?-快慢指針-
基本思路是將鏈表的后半部分反轉(zhuǎn),再比較前后兩部分是否相同。
當(dāng)然可以遍歷鏈表得到鏈表長度,再取中間結(jié)點,但是這里使用了快慢指針的奇特方法。即創(chuàng)建快指針和慢指針,初始都指向head,慢指針每次移動一個結(jié)點,快指針每次移動兩個結(jié)點直到快指針的下一次移動指向NULL,此時慢指針指向的結(jié)點就是中間結(jié)點。若鏈表結(jié)點為奇數(shù)個,易得中心結(jié)點不影響后續(xù)結(jié)果。
struct ListNode* reverseList(struct ListNode* head) {struct ListNode* prev = NULL;struct ListNode* curr = head;while (curr != NULL) {struct ListNode* nextTemp = curr->next;curr->next = prev;prev = curr;curr = nextTemp;}return prev; }//三指針法反轉(zhuǎn)鏈表struct ListNode* endOfFirstHalf(struct ListNode* head) {struct ListNode* fast = head;struct ListNode* slow = head;while (fast->next != NULL && fast->next->next != NULL) {fast = fast->next->next;slow = slow->next;}return slow; }//使用快慢指針找到鏈表的中心位置bool isPalindrome(struct ListNode* head) {if (head == NULL) {return true;}// 找到前半部分鏈表的尾節(jié)點并反轉(zhuǎn)后半部分鏈表struct ListNode* firstHalfEnd = endOfFirstHalf(head);struct ListNode* secondHalfStart = reverseList(firstHalfEnd->next);// 判斷是否回文struct ListNode* p1 = head;struct ListNode* p2 = secondHalfStart;bool result = true;while (result && p2 != NULL) {if (p1->val != p2->val) {result = false;}p1 = p1->next;p2 = p2->next;}// 還原鏈表并返回結(jié)果firstHalfEnd->next = reverseList(secondHalfStart);return result; }? ? ? ? ?時間復(fù)雜度與空間復(fù)雜度的控制是算法的精髓,上面三個方法有不同的時間,空間復(fù)雜度,因為博主這方面了解尚淺,故不在這里具體分析。
總結(jié)
以上是生活随笔為你收集整理的判断回文链表(剑指offer.027)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 链表的建立,搜索,插入,反转,销毁以及合
- 下一篇: 哈希表(散列表)的介绍,代码实现