牢騷:本篇博客兩個星期前已經存為草稿,鑒於發生一些糟糕的事情,今天才基本完成。本人6月份應屆畢業生一枚,畢業后當天來到帝都,之后也非常順利,面試了倆家公司都成功了。一家做C++方面電商ERP,一家做wifi模塊,覺得第二家公司小,薪資低,但是覺得好玩就去了。同時,在學校也喝了不少雞湯,覺得公司小怎么了。然而去了不到20天,公司被深圳一家公司收購了,公司動員我去深圳,我尼瑪我才來20多天啊,有木有?而且感覺公司做這么大的決定都是隨時拍板的嗎?
原本以為一個公司的生命力強到可以忽略的概率,然而當自己真實的遇到這一切,才知道這個社會有多殘忍,公司的生存可能比一個員工的生存更要困難。
接下來就是找工作了,跑呀,找呀。。。。。。。。 一路被忽略,一路被放鴿子,一路被無視。。。。。。。 心里五味雜陳。。。。。。。。。
so,可能我還是太菜了,這種玩計算機的心態,也許會被生活玩,唯有認真拼命的學習也許能夠拯救我的頹勢。
感謝seven,嚴厲也好,鼓勵也好,短短的一個月,您教會了我許多,感謝您的知遇之恩!願你們去深圳一切順利!待倔強的我在北京玩上一陣!
————————————————————————————————————————————————————————————————————————————————
鏈表是數據結構中比較基礎也是比較重要的類型之一,那么有了數組,為什么我們還需要鏈表呢!或者說設計鏈表這種數據結構的初衷在哪里?
這是因為,在我們使用數組的時候,需要預先設定目標群體的個數,也即數組容量的大小,然而實時情況下我們目標的個數我們是不確定的,因此我們總是要把數組的容量設置的很大,這樣以來就浪費了很多的空間。另外,數組在進行插入操作和刪除操作的時候,在插入或者刪除制定元素之后,我們往往需要進行循環移位,這增加了我們的線性開銷。
正是由於以上的兩種主要原因,鏈表被設計出來用於一般表的操作。為了避免上面描述數組的兩種弊端,我們希望鏈表有一下的特點
1 可以靈活的擴展自己的長度。
2 存儲地址不連續,刪除或者插入操作的時候不需要循環移位。
要實現以上兩個特點,我們需既要保證每個節點的獨立性,又要保存相鄰兩個節點的聯系。
為此,鏈表一般被設計為下面的形式。
Node--->Node---->Node
鏈表是由一個一個的節點組成的,可以方便和自由的插入未知個Node,前一個節點中用指針保存着下一個節點的位置,這樣以來便順利的完成了我們對鏈表的兩點期望,但是唯一的缺點是增加了額外的空間消耗。
————————————————————————————————————————————————————————————————————————————
鏈表的定義:
鏈表的定義一般使用結構體,在看《數據結構與算法分析》這本書的時候發現,書中頻繁的使用typedef的關鍵字,結果真的很棒不僅保持的代碼的整潔程度,也讓我們在下面的編碼過程中少見了很多煩人的指針(當然指針還是一直存在的)。所以這里也借用了書中的定義方法。
struct Node; typedef struct Node* PtrNode; typedef PtrNode Position; typedef PtrNode List; struct Node{ int Value; PtrNode Next; };
下面接着書寫一個建立鏈表的函數,輸入每個節點的值,直到這個值是-1的時候函數結束。
在這個里面,我以前一直搞不明白為什么需要定義三個Node *,現在終於了解了,最終還是復習了指針的內容明白的,這里說一下指針實現鏈表對指針的操作很頻繁,需要比較扎實的掌握了指針之后,在來看鏈表會輕松很多。在下面的一段程序里,我分別定義了head/p/tmp這三個指向節點結構體的指針,head的主要作用就像一個傳銷頭目,他會主動聯系上一個下線p,然后他就什么也不干了,p接着去發展一個又一個的下線tmp,結果一串以head為首的鏈表就出來了。
起先,我總覺得有了head,為什么還要p,這是因為如果直接使用head去指向下一個節點,head的位置也是不斷在移動的,即它永遠處於鏈表的尾端,這樣當我們返回鏈表的時候,其實是空值。所以,我們需要p這個中轉環節。(其實,這種做法在指針中非常普遍,大部分有返回指針類型的函數中,都會首先定義一個指針變量來保存函數的傳入的參數,而不是對參數直接進行操作)。
/* 函數功能:創建一個鏈表 函數描述:每次輸入一個新的整數,即把新增加一個節點存放該整數, 當輸入的整數為-1時,函數結束。 */ List create() { int n=0; Position p,head,tmp; head=NULL; tmp=malloc(sizeof(struct Node)); if(tmp==NULL) { printf("tmp malloc failed!\n"); return NULL; } else { p=tmp; printf("please input the first node's message!\n"); scanf("%d",&(tmp->Value)); } while(tmp->Value!=-1) { n+=1; if(n==1) { head=p; tmp->Next=NULL; } else { p->Next=tmp; } p=tmp; tmp=malloc(sizeof(struct Node)); printf("please input the %d node!\n",n+1); scanf("%d",&(tmp->Value)); } p->Next=NULL; free(tmp); //free函數free掉的只是申請的空間,但是指針還是依然存在的。 tmp=NULL; return head; }
接下來,在寫一個刪除鏈表節點的函數,輸入一個整數然后遍歷鏈表節點,當鏈表節點的值與該整數相等的時候,即把該節點刪除。
在完成這個函數首先一定要把這個過程思考清楚,不可否認我之前是一個上來就敲代碼的人,看了《劍指offer》感覺這種習慣是程序員的大忌,甚至還想寫一篇博客,名字都想好了《程序員的自我修養之思考在前,代碼在后》。其實想想也是,我們寫程序的目的是為了解決問題,而不是為了簡單的寫程序,純粹的讓程序跑起來大概只會在上學那會存在吧!真實的程序開發中需要考慮幾乎所有 能想到的實際問題,所以無論程序再下,一要學會先思考清楚,再下筆寫程序。
關於這個函數,我們要想到的是:
1 如果鏈表為空,我們該怎么做,當然是直接返回。
2 如果要刪除的元素為頭節點該怎么辦?
3 如果要刪除的元素為尾節點該怎么辦?
當注意到以上三個部分,我們的程序就可能避免掉了輸入鏈表為空,程序直接崩潰的現象,也可以避免刪除元素值為頭節點時刪不掉的尷尬。我們的程序就有了一定的魯棒性。
下面着重考慮鏈表的刪除的實現:
list: Node_a->Node_b->Node_c->Node_d;
list tmp p
-------> tmp->Next=p->Next;
list: Node_a->Node_b----------->Node_d
free(p)
假設我們要刪除的節點為上圖的Node_c;假設我們能夠找到Node_c的前一個位置tmp和被刪除節點位置p的話;這個時候我們只需要執行tmp->Next=p->Next即可。
只要完成上面的分析以及考慮到各種情況,我們完成下面的代碼就水到渠成了。
/* 函數功能:刪除鏈表中指定值的節點(如果存在多個,只刪除第一個) 本例中輸入一個整數,刪除鏈表節點值為這個整數的節點。 */ List DeleteNode(List list) { Position p,tmp; int value; if(list==NULL) { printf("The list is null,function return!\n"); return NULL; } else { printf("please input the delete Node's value:\n"); scanf("%d",&value); } p=list; if(p->Value==value) { list=p->Next; free(p); p=NULL; return list; } while(p!=NULL&&p->Value!=value) { tmp=p; p=p->Next; } if(p->Value==value) { if(p->Next!=NULL){ tmp->Next=p->Next; } else { tmp->Next=NULL; } free(p); p=NULL; } return list; }
關於鏈表的使用場景分析:
鏈表在程序開發中用到的頻率還是非常高的,所以在高級語言中往往會對鏈表進行一些實現,比如STL中list以及Java中也有類似的東西。在目前的服務器端開發,主要運用鏈表來接收一些從數據中取出來的數據進行處理。
即使你不知道鏈表的底層實現,仍然可以成功的運用STL里面的現成的東西。但是作為一個學習者,我覺得會使用和從底層掌握仍然是兩個不同的概念,linux之父說:“talk is less,show you code”。
以下的程序,用鏈表模擬了一個電話通訊錄的功能,包括添加聯系人,查找聯系人,以及刪除聯系人。
PS:關於魯棒性,程序中最大的危險是使用了gets這個函數,目前先保留使用gets,等待找到工作之后在做進一步的程序完善。
/************************************************************************** Programe: This is a phone list write by list The programe is just prictise for list Author: heat nan Mail:964465194@qq.com Data:2015/07/27 **************************************************************************/ #include<stdio.h> #include<string.h> #include<stdlib.h> #define N 25 #define M 15 struct node; typedef struct node* p_node; typedef p_node List; typedef p_node Position; typedef struct node** PList; struct node{ char name[N]; char number[M]; Position next; }; int JudgeNameExist(List list,char* name); void AddPerson(PList list); void PrintList(List list); List FindPerson(List list); List FindPersonByName(List list,char* name); int AddPersonByName(PList list,List node); int DeletePersonByName(PList list,char* name); void DeletePerson(PList list); int main() { List list=NULL; Position p; char cmd[100]; while(1) { printf(" MAIN \n"); printf(" ******* 1 add a person *******\n"); printf(" ******* 2 show the phone list *******\n"); printf(" ******* 3 find from phone list *******\n"); printf(" ******* 4 delete from phone list *******\n\n\n"); printf("Please input the cmd number:\n"); gets(cmd); switch(cmd[0]) { case '1': AddPerson(&list); break; case '2': PrintList(list); break; case '3': FindPerson(list); break; case '4': DeletePerson(&list); break; default: printf("wrong cmd!\n"); break; } } return 0; } /* Function:判斷要添加的聯系人名稱是否已經存在於電話簿中. Input: List 電話列表,name 要添加的聯系人的姓名. Return: 已經存在返回1,不存在返回0. */ int JudgeNameExist(List list,char* name) { if(FindPersonByName(list,name)!=NULL) return 1; else return 0; } /* Function:根據輸入的姓名查找聯系人的信息節點 Input: 要輸入的電話列表list,姓名name Return: 返回查找到的節點 */ List FindPersonByName(List list,char* name) { while(list!=NULL) { if(strcmp(list->name,name)==0) break; list=list->next; } return list; } /* Function:根據姓名添加新的聯系人到聯系人列表 Input: 指向聯系人列表地址的指針, 新用戶節點 Return: 添加成功返回1,添加失敗返回0 */ int AddPersonByName(PList list,List node) { if(node==NULL) { printf("the node is NULL!\n"); return 0; } if(*list==NULL) { *list=node; return 1; } List pHead=*list; while(pHead->next!=NULL) pHead=pHead->next; pHead->next=node; return 1; } void AddPerson(PList list) { Position tmp; Position p_head; tmp=(struct node*)malloc(sizeof(struct node)); char name[N]; char number[M]; if(tmp==NULL) { printf("malloc the tmp node failed in function add person!\n"); } else { printf("please input the name:\n"); gets(name); printf("please input the number:\n"); gets(number); strcpy(tmp->name,name); strcpy(tmp->number,number); tmp->next=NULL; } if(JudgeNameExist(*list,name)==1) { free(tmp); printf("the name have already exist!\n"); return; } AddPersonByName(list,tmp); } /* Function: 打印聯系人列表 Input: 聯系人列表 */ void PrintList(List list) { Position show; show=list; if(show==NULL) { return ; } printf("Now,we print the phone list:\n"); while(show!=NULL) { printf("Name:%s Number:%s\n",show->name,show->number); show=show->next; } } List FindPerson(List list) { char name[N]; Position pHead=list; printf("please input the name you will find:\n"); gets(name); Position node=FindPersonByName(list,name); if(node!=NULL) printf("find success! name-> %s number-> %s\n",node->name,node->number); else printf("find failed!\n"); return node; } /* Function:根據姓名刪除聯系人 Input: 指向聯系人地址的指針,聯系人姓名 Output: 刪除成功返回1,失敗返回0 */ int DeletePersonByName(PList list,char* name) { if(*list==NULL||name==NULL) return 0; List pHead=*list; if(strcmp(pHead->name,name)==0) { *list=pHead->next; free(pHead); pHead->next==NULL; return 0; } List tmp=pHead->next; while(tmp!=NULL) { if(strcmp(tmp->name,name)==0) { pHead->next=tmp->next; free(tmp); tmp->next=NULL; return 1; } pHead=tmp; tmp=tmp->next; } return 0; } void DeletePerson(PList list) { List pHead=*list; if(pHead==NULL) { printf("there is no person you can delet\n"); return ; } char name[N]; printf("please input the name:\n"); gets(name); DeletePersonByName(list,name); }