翻轉鏈表作為,鏈表的常用操作,也是面試常遇到的。
分析
非遞歸分析:
非遞歸用的小技巧比較多,很容易出錯。
遞歸分析比較簡單,在代碼裡面
代碼:
#include<stdio.h> #include<stdlib.h> typedef int elemtype; typedef struct node{ elemtype element; struct node*next;//寫成node* next;node * next;node *next;也是可以的,為了方便閱讀,以後統一寫成elemtype* element。'*',';',',''=','->'等等這些特殊意義的關鍵字符語法規則差不多,本身具有分割意義,兩旁加空格與不加空格的意義是一致的,也就是說空格對這些特殊字符是不起作用的,而解析編譯器,運行器也不是利用空格來分割的;標志符是不能用這些字符,為了方便閱讀,不會看的一駝屎,最好在這些特殊的字符兩旁加空格。 }node;//這個類型名與其的結構體名可以一樣,不矛盾。 /*Function:將鏈表所有結點從頭結點順序打印,遍歷鏈表。traverse_linkedList *遍歷一種數據機構,進去看一看,給人說一說,一般遍歷打印裡面的數據,知道是進去,可以拿到裡面的數據。 *這種數據結構有很多數據元素,但每次遍歷只能訪問一個元素,因此要循環,而且要訪問所有的結點,必須有一種機制從一個結點跳到另一個結點。 *@paramb:node* phead :鏈表首地址,首結點地址。 */ void traverse_linkedList(node* phead){ node *p=phead ;//定義一個跳轉控制指針 if (NULL == p){ printf("親,鏈表為空\n"); return ; } else{ while(NULL != p){ printf("%d\n",p -> element);//訪問結構體的數據,可以用: 其結構體變量.成員變量;還可以:指向這個結構體的指針 -> 成員變量 p = p -> next ; } } } /*Function:翻轉鏈表(非遞歸) reverse_linkedList *param:node** phead //參數意義:保存鏈表指針變量的地址 *return: void * *知識點:node** p;p代表鏈表指針變量的地址。*p才是鏈表。**p 代表結點(可以認為"*"具有解析功能) *整體的思路:重整結點的關系,原來從左指向右,翻轉後從右指向左;原來指向頭結點,翻轉後指向尾結點 * *時間復雜度:O(n) * * * */ void reverse_linkedList(node** phead){//存鏈表頭的指針變量的地址傳過來,旨在能改變這個變量,指針本質上是存地址的變量。 node *pLeft,*pRight,*pMiddle;//定義三個指針: 左中右,pRight:作移動。 pLeft=pRight=*phead; //空鏈表或者只有一個結點 if(NULL == pLeft || NULL == ( pLeft -> next)){ return ; }else{ //頭的結點的處理 pLeft = *phead ; pRight = (*phead) -> next ; //' ->'優先級最高 pLeft -> next = NULL ; //循環控制條件 node *ctl=pRight; while(NULL != ctl){ pMiddle = pRight ;// 在移動前,先保存 pRight = pRight -> next ;// pRight移動到下一個結點 ctl = pMiddle -> next; //在變換關系前,先把控制的信息存起來 pMiddle -> next = pLeft ; //變換關系 pLeft = pMiddle ; // p變換 } *phead = pLeft ; //指向鏈表頭指針變量,指向新的頭。 } } /* *Function:翻轉鏈表(遞歸) reverse_recursive_linkedList *param:node* head *return: node* * *知識點: *遞歸:自身調用自身;出口。 *思考: * *如果鏈表是空鏈表或者只有一個結點,就可以直接的返回表頭;如果是兩個結點以上鏈表,調用自身,拿到子結點的表頭,再重建他們的關系。 * * */ node* reverse_recursive_linkedList(node * head){ if(head == NULL || head -> next == NULL){ return head ; } else{ node* second = head -> next ; node* new_head = reverse_recursive_linkedList(second); second -> next = head ; head -> next = NULL ; return new_head ; } } int main() { node *p , *q , *head;//僅僅定義了幾個指針變量,系統做了哪些工作呢?開辟這些指針變量類型所需的空間。若沒有初始值一般由系統隨機分配值,還沒有指向真正的結點,也就是說 p -> 成員變量是沒有意義的。會報""; node 類型的聲明,告訴系統作對應的語法檢查,該怎麼處理指針指向的內容。 // insert 10 nodes //創建結點,結點本質上是數據塊,這些數據是占用空間的,有了儲存空間,就有了數據載體。所以開辟空間是創建數據的關鍵,然後告訴系統你怎麼來處理這塊空間,最後空間開辟成功。 p = q = (node* )malloc(sizeof(node)) ;//與” 數據類型 標識符“ 創建的差別,無標識符,要手動開空間,並提取信息。標識符功能:解析+地址。 head = p ; p -> element = 0 ; int i=0; for ( i = 0 ;i < 9 ; i++){ q = (node* )malloc(sizeof(node)) ; q -> element = i + 1 ; p -> next = q ; p = q ; } traverse_linkedList(head); reverse_linkedList(&head);//&只能作取址用,沒有解析功能。 traverse_linkedList(head); traverse_linkedList(reverse_recursive_linkedList(head)); }
本人在重拾C,很多東西看是熟悉而又陌生,所以注釋比較多一點,僅供參考,不爽直接忽略,