1. 程式人生 > >C語言資料結構單鏈表之溫故而知新

C語言資料結構單鏈表之溫故而知新

拋棄繁雜的定義,以實用,實戰的角度來學習資料結構,這將使得資料結構的學習非常的簡單。

前面已經學習了單鏈表的建立操作:http://blog.csdn.net/morixinguan/article/details/68951912

這節,將單鏈表溫習的筆記共享出來,然後寫一個例子,以防自己忘記。

1、單鏈表的資料結構的定義:


建立節點函式原型可定義如下:
struct list *create_node(int data) ;
如何建立單鏈表的節點,主要分以下步驟:
(1)給當前的每個節點的資料結構配置定量的空間大小
   ep : struct list *node = malloc(sizeof(struct list));
(2)清節點資料(由於結構體變數在未初始化的時候,資料是髒的)
   ep : memset(node,0,sizeof(struct list));
(3)給節點初始化資料
   ep : node->id = data ; 
(4)將該節點的指標域設定為NULL
   ep : node->next = NULL ;
3、單鏈表的尾插:
尾插節點函式原型可定義如下:


如何將當前連結串列和新的節點相連線?只要實現:
header->next = new 

尾插流程如下:

(1)獲取當前節點的位置,也就是訪問頭節點
   ep : struct list *p = header ;
(2)判斷是否為最後一個節點,如果不是,移動到下一個節點,如果是,將資料插入尾部。
   ep : while(NULL != p->next) p = p->next ;
	    p->next = new ;
4、單鏈表的頭插

很好理解,頭插就是把新的節點插在原來的節點和原來節點的下一個節點之間的一個節點。如圖,新的節點插在頭節點和節點1。
所以可以推出頭插流程如下:
(1)獲取當前節點的位置,也就是訪問頭節點
	ep : struct list *p = header ;
(2)新的節點的下一個節點設定為原來頭節點的下一個節點(第一個節點)
    ep : new->next = p->next ;
(3)原來的頭節點的下一個節點設定為現在新插入的頭節點
	ep : p->next = new ;
5、單向連結串列的遍歷


      如圖為一條單向連結串列的模型,看圖知道該連結串列由頭節點和若干個節點組成,最後一個節點(尾節點)為NULL 。
從圖中可以得出資訊,如果我們要打印出各個節點的資料,要考慮以下問題:
(1)需要列印頭節點嗎?(頭節點肯定是不用列印的,因為這是我們為了操作方便而設定的一個節點)。
(2)這條連結串列有多少個節點我們怎麼知道?(通過判斷該連結串列是否已經到達了尾節點,標誌就是NULL)
那麼可以得到流程如下:
(1)獲取當前節點的位置,也就是訪問頭節點
	ep : struct list *p = header ;
(2)由於頭節點我們不需要去列印它,這時候,初始化列印的節點需要從第一個節點開始。
	ep : p = p->next ;  
(3)判斷是否為最後一個節點,如果不是,先列印第一個節點的資料(1),然後移動到下一個節點(2),重複這兩個步驟。
   如果是最後一個節點,直接列印資料即可。
	while(NULL != p->next){ printf("node:%d\n",p->data) ; p = p->next ;}
	printf("node:%d\n",p->data);
	當然還可以一句程式碼解決,這樣就達到了先偏移,後取資料。
	while(NULL != p->next){ p = p->next ; printf("node:%d\n",p->data) ; }
6、單向連結串列的刪除

刪除節點的函式原型可定義如下:
int detele_list_node(struct list *pH , int data);
單向連結串列的刪除要考慮兩種情況,一種的普通節點的刪除(當然,頭節點不能算)
還有一種是尾節點的前一個節點的刪除情況,注意,刪除完節點還需要釋放對應節點的記憶體空間。



刪除節點的設計流程:
(1)先定義兩個指標,一個表示當前的節點,另一個表示當前節點的上一個節點。
	ep : struct list *p = header ;  //當前節點
		 struct list *prev = NULL ; //當前節點的上一個節點
(2)遍歷整個連結串列,同時儲存當前節點的前一個節點
    ep : while(NULL != p->next)
		{ 
		  //儲存了當前的節點的前一個節點
		  prev = p ;  
		  //儲存當前偏移的節點
		  p = p->next ; 
		  return 0 ;
		}
(3)在遍歷的過程中查詢要刪除的資料
	ep : while(NULL != p->next)
		{ 
		  //儲存了當前的節點的前一個節點
		  prev = p ;  
		  //儲存當前偏移的節點
		  p = p->next ; 
		  //查詢到了資料
		  if(p->id == data)
		  {
		  
		  }
		  return 0 ;
		}
(4)查詢到了資料後,分兩種情況刪除
	ep : 普通節點的刪除
		if(p->id == data)
		{
			prev->next = p->next ;
			free(p);
		}
	ep : 考慮尾節點的下一個節點為NULL的節點刪除
		if(p->id == data)
		{
			if(p->next == NULL)
			{
				prev->next = NULL ;
				free(p);
			}
		}
5、單向連結串列的逆序操作


逆序步驟:


流程咱們基本搞懂了,下面寫一個程式,這將會變得非常非常的簡單。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct slist
{
	int id ;
	struct slist *next ;			
}L;

//建立一個節點 
L *create_node(int data)
{
	//給每個節點分配結構體一樣的空間大小 
	L *p = malloc(sizeof(L));
	if(NULL == p)
	{
		printf("malloc error!\n");
		return NULL ;
	}
	//由於結構體在未初始化的時候一樣是髒資料,所以要清 
	memset(p,0,sizeof(L));
	//初始化第一個節點 
	p->id = data ; 
	//將節點的後繼指標設定為NULL 
	p->next = NULL ;
}

//連結串列的尾插 
void tail_insert(L *pH , L *new)
{
	//獲取當前的位置 
	L *p = pH ; 
	//如果當前位置的下一個節點不為空 
	while(NULL != p->next)
	{
		//移動到下一個節點 
		p = p->next ;
	}
	//如果跳出以上迴圈,所以已經到了NULL的這個位置
	//此時直接把新插入的節點賦值給NULL這個位置 
	p->next = new ;
}

//連結串列的頭插 
void top_insert(L *pH , L *new)
{
	L *p = pH ;
	new->next = p->next ;
	p->next = new ;
}

//連結串列的遍歷 
void Print_node(L *pH)
{
	//獲取當前的位置 
	L *p = pH ;
	//獲取第一個節點的位置 
	p = p->next ;
	//如果當前位置的下一個節點不為空 
	while(NULL != p->next)
	{
		//(1)列印節點的資料 
		printf("id:%d\n",p->id);
		//(2)移動到下一個節點,如果條件仍為真,則重複(1),再(2) 
		p = p->next ;
	}
	//如果當前位置的下一個節點為空,則列印資料
	//說明只有一個節點 
	printf("id:%d\n",p->id);
}

//刪除連結串列中的節點 
int detele_list_node(L * pH , int data)
{
	//獲取當前頭節點的位置 
	L *p = pH ;
	L *prev = NULL;
	while(NULL != p->next)
	{
		//儲存當前節點的前一個節點的指標 
		prev = p ;
		//然後讓當前的指標繼續往後移動 
		p = p->next ; 	
		//判斷,找到了要刪除的資料  
		if(p->id == data)
		{
			//兩種情況,一種是普通節點,還有一種是尾節點
			if(p->next != NULL)  //普通節點的情況 
			{
				prev->next = p->next ;
				free(p);
			}
			else //尾節點的情況 
			{
				prev->next = NULL ; //將這個尾節點的上一個節點的指標域指向空 
				free(p); 
			}
			return 0  ;
		}
	}
	printf("沒有要刪除的節點\n");
	return -1 ;
}

void trave_list(L * pH)
{
	//儲存第一個節點的位置 
	L *p = pH->next;
	L *pBack;
	int i = 0 ;
	if(p->next == NULL || p == NULL)
		return ;
		
	while(NULL != p->next) //遍歷連結串列 
	{
		//儲存第一個節點的下一個節點 
		pBack = p->next ; 
		//找到第一個有效節點,其實就是頭指標的下一個節點 
		if(p == pH->next) 
		{
			//第一個有效節點就是最後一個節點,所以要指向NULL 
			p->next = NULL ; 
		} 
		else
		{
			/*
			new->next = p->next ;
			p->next = new ;
			*/
			p->next = pH->next ; //尾部連線 
		}
		pH->next = p ; //頭部連線 
		p = pBack ; //走下一個節點 
	}
	top_insert(pH,p); //插入最後一個節點 
}

int main(int argc , char **argv) 
{
	//建立第一個節點 
	int i ;
	L *header = create_node(0); 
	for(i = 1 ; i < 10 ; i++)
	{
		tail_insert(header,create_node(i));
	}
	Print_node(header);
	detele_list_node(header,5);
	putchar('\n');
	Print_node(header);
	putchar('\n');
	trave_list(header);
	Print_node(header);
	return 0 ;
}
執行結果: