數據結構(C語言版)第四章:鏈表

4.1 指針 node

實際上並不推薦使用malloc和free,使用malloc還簡單,可是當指針進行移動的時候,你如何肯定在哪一個地址上free內存,則是個大難題. 算法

咱們寫個正常的malloc和free程序: 編程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main( void )
{
	int *pi = ( int * )malloc( sizeof( int ) );
	*pi = 4;
	printf("%d\n", *pi );
	free( pi );

	return 0;
}



而後寫個比較怪怪的函數:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main( void )
{
	char **pstr = ( char ** )malloc( sizeof( char ) * 100 );
	*pstr = "hello world";
	printf("%s\n", *pstr );
	free( pstr );

	return 0;
}



這段代碼至少對目前的我來講,有點怪異(雖然是我本身寫出來的).寫這段代碼有如下的含義:

1. 對於char *,最好理解爲字符串,而不是字符指針(雖然操做的時候,能夠經過字符指針進行操做) 數組

2. 對於malloc函數,要分配肯定的大小,因此程序中爲sizeof(char)而不是sizeof(char*),由於指針在特定的機子上的大小不一樣(通常爲4字節) 數據結構

3. 在free中,必定要找對free的起始地址.好比我若是這樣修改,則會報錯: 模塊化

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main( void )
{
	char **pstr = ( char ** )malloc( sizeof( char ) * 100 );
	*pstr = "hello world";
	printf("%c---%s---%p\n", **pstr, *pstr, pstr );
	pstr++;

	free( pstr );

	return 0;
}



程序很恐怖的輸出:

你能確保在編寫代碼中,用到free的時候,不會一不當心的移動了一下指針??至少我基本不敢用free,也是這個緣由.這也致使了實際上我也不太敢用malloc. 函數


4.2 鏈表 性能

簡單寫一個單鏈表,數據元素爲字符串,支持增長,刪除,查找.按字符串的字典順序排序. 設計

PS:實際上這裏的難度在於:C語言只能傳值,因此你必須傳遞指針的指針到函數中去.而傳遞指針的指針也很麻煩,那麼咱們能夠這樣想:用一個固定的節點看成頭指針,而實際的頭節點看成第二個指針便可: 指針

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct LINK{
	char				*word;
	struct LINK		*next;
}Link;

void insert( Link *link, char *word )
{
	Link *temp = ( Link * )malloc( sizeof( Link ) );	//用於存儲新的值
	Link *tempLink = ( Link * )malloc( sizeof( Link ) );	//在插入節點時存儲上一個節點
	temp->word = word;
	temp->next = NULL;
	if ( NULL == link->next ){
		link->next = temp;
	}
	else{
		tempLink = link;				//存儲上一個節點
		link = link->next;
		while ( NULL != link ){
			if ( -1 == strcmp( link->word, word ) ){
				tempLink = link;
				link = link->next;
			}
			else if ( 0 == strcmp( link->word, word ) ){
				return;
			}
			else{
				temp->next = link;
				tempLink->next = temp;
				return;
			}
		}
		if ( NULL == link ){					//判斷爲尾節點的特殊狀況
			tempLink->next = temp;
		}
	}
}

int delete( Link *link, char *word )
{
	Link *tempLink = ( Link * )malloc( sizeof( Link ) );		//用於存儲刪除元素的上一個節點
	tempLink = link;
	link = link->next;
	while ( NULL != link ){
		if ( 0 != strcmp( link->word, word ) ){
			tempLink = link;
			link = link->next;
		}
		else{
			tempLink->next = link->next;
			return 1;
		}
	}

	return 0;
}

void show( Link *link ){
	while ( NULL != link ){
		printf("%s-->", link->word );
		link = link->next;
	}

	printf("NULL\n");
}

int main( void )
{
	Link *link = ( Link * )malloc( sizeof( Link ) );
	link->next = NULL;
	link->word = "EOF";		//這裏假定沒有字符串等於EOF
	insert( link, "cc" );
	insert( link, "dd" );
	insert( link, "aa" );
	insert( link, "bb" );
	insert( link, "ba" );
	insert( link, "cb" );

	show( link->next );

	delete( link, "bb" );
	delete( link, "dd" );
	delete( link, "ff" );

	show( link->next );

	return 0;
}



程序輸出:

有道題比較難(真的寫的時候也不難,只是以前我沒寫出來,如今寫出來罷了),就是將鏈表進行翻轉,但不能借助其餘的存儲空間(若是能的話,至關於對delete一個鏈表再insert成一個鏈表),代碼以下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct LINK{
	char				*word;
	struct LINK		*next;
}Link;

void insert( Link *link, char *word )
{
	Link *temp = ( Link * )malloc( sizeof( Link ) );	//用於存儲新的值
	Link *tempLink = ( Link * )malloc( sizeof( Link ) );	//在插入節點時存儲上一個節點
	temp->word = word;
	temp->next = NULL;
	if ( NULL == link->next ){
		link->next = temp;
	}
	else{
		tempLink = link;				//存儲上一個節點
		link = link->next;
		while ( NULL != link ){
			if ( -1 == strcmp( link->word, word ) ){
				tempLink = link;
				link = link->next;
			}
			else if ( 0 == strcmp( link->word, word ) ){
				return;
			}
			else{
				temp->next = link;
				tempLink->next = temp;
				return;
			}
		}
		if ( NULL == link ){					//判斷爲尾節點的特殊狀況
			tempLink->next = temp;
		}
	}
}

int delete( Link *link, char *word )
{
	Link *tempLink = ( Link * )malloc( sizeof( Link ) );		//用於存儲刪除元素的上一個節點
	tempLink = link;
	link = link->next;
	while ( NULL != link ){
		if ( 0 != strcmp( link->word, word ) ){
			tempLink = link;
			link = link->next;
		}
		else{
			tempLink->next = link->next;
			return 1;
		}
	}

	return 0;
}

void show( Link *link ){
	while ( NULL != link ){
		printf("%s-->", link->word );
		link = link->next;
	}

	printf("NULL\n");
}

void reverse( Link *link )
{
	Link *tail = ( Link * )malloc( sizeof( Link ) );
	Link *mid = ( Link * )malloc( sizeof( Link ) );
	Link *prev = ( Link * )malloc( sizeof( Link ) );
	Link *head = ( Link * )malloc( sizeof( Link ) );
	head = link;
	tail = NULL;
	link = link->next;
	if ( NULL != link ){
		mid = link;
		prev = link->next;
		while ( NULL != prev ){
			mid->next = tail;
			tail = mid;
			mid = prev;
			prev = prev->next;
		}
		mid->next = tail;
		head->next = mid;
	}
}

int main( void )
{
	Link *link = ( Link * )malloc( sizeof( Link ) );
	link->next = NULL;
	link->word = "EOF";		//這裏假定沒有字符串等於EOF
	insert( link, "cc" );
	insert( link, "dd" );
	insert( link, "aa" );
	insert( link, "bb" );
	insert( link, "ba" );
	insert( link, "cb" );

	show( link->next );

	delete( link, "bb" );
	delete( link, "dd" );
	delete( link, "ff" );

	show( link->next );

	reverse( link );
	show( link->next );

	return 0;
}



程序輸出:


4.3 動態鏈棧與動態鏈隊列

動態鏈棧與動態鏈隊列沒什麼特別的地方,主要就是多個鏈棧的集合而已,而傳遞的時候只要把每一個棧的地址傳遞到函數裏面去就能夠了.


4.4 多項式

多項式的相加:書上的方法很牛,它是直接返回一個地址的.效率在我看來,是全部多項式相加里面最高的.可是我我的感受,代碼應該簡潔,更應該有良好的閱讀感.因此我打算是將相加的結果看成一個指針傳遞進去,而不是從函數內返回回來:

先把本身寫的錯誤的代碼粘貼出來,用於銘記(對待C,C++,都要用及其認真的態度來對待):

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct POLYNODE{
	int							coef;
	int							expon;
	struct POLYNODE	*next;
}PolyNode;

void padd( PolyNode *exp1, PolyNode *exp2, PolyNode *exp )
{
	PolyNode *addNode;

	while ( exp1 && exp2 ){
		addNode = ( PolyNode * )malloc( sizeof( PolyNode ) );
		if ( exp1->expon < exp2->expon ){
			addNode->coef = exp2->coef;
			addNode->expon = exp2->expon;
			addNode->next = NULL;
			exp = addNode;
			exp = exp->next;
			exp2 = exp2->next;
			continue;
		}
		else if ( exp1->expon == exp2->expon ){
			if ( 0 == ( exp1->coef + exp2->coef ) ){
				exp1 = exp1->next;
				exp2 = exp2->next;
				continue;
			}
			addNode->coef = exp1->coef + exp2->coef;
			addNode->expon =  exp1->expon;
			addNode->next = NULL;
			exp = addNode;
			exp = exp->next;
			exp1 = exp1->next;
			exp2 = exp2->next;
			continue;
		}
		else{
			addNode->coef = exp1->coef;
			addNode->expon = exp1->expon;
			addNode->next = NULL;
			exp = addNode;
			exp = exp->next;
			exp1 = exp1->next;
			continue;
		}
	}

	while ( exp1 ){
		addNode = ( PolyNode * )malloc( sizeof( PolyNode ) );
		addNode->coef = exp1->coef;
		addNode->expon = exp1->expon;
		addNode->next = NULL;
		exp = addNode;
		exp = exp->next;
		exp1 = exp1->next;
	}

	while ( exp2 ){
		addNode = ( PolyNode * )malloc( sizeof( PolyNode ) );
		addNode->coef = exp2->coef;
		addNode->expon = exp2->expon;
		addNode->next = NULL;
		exp = addNode;
		exp = exp->next;
		exp2 = exp2->next;
	}
}

void show( PolyNode *exp )
{
	exp = exp->next;
	while ( exp ){
		printf("%d * x^%d + ", exp->coef, exp->expon );
		exp = exp->next;
	}
	printf("0\n");
}

int main( void )
{
	PolyNode *exp1 = ( PolyNode * )malloc( sizeof( PolyNode ) );
	PolyNode *exp2 = ( PolyNode * )malloc( sizeof( PolyNode ) );
	PolyNode *exp = ( PolyNode * )malloc( sizeof( PolyNode ) );
	PolyNode node1_1, node1_2, node1_3, node2_1, node2_2, node2_3;
	exp1->next = NULL;
	exp2->next = NULL;
	exp->next = NULL;

	node1_1.coef = 3;
	node1_1.expon = 14;
	node1_1.next = &node1_2;
	node1_2.coef = 2;
	node1_2.expon = 8;
	node1_2.next = &node1_3;
	node1_3.coef = 1;
	node1_3.expon = 0;
	node1_3.next = NULL;

	node2_1.coef = 8;
	node2_1.expon = 14;
	node2_1.next = &node2_2;
	node2_2.coef = -3;
	node2_2.expon = 10;
	node2_2.next = &node2_3;
	node2_3.coef = 10;
	node2_3.expon = 6;
	node2_3.next = NULL;

	exp1->next = &node1_1;
	exp2->next = &node2_1;

	padd( exp1->next, exp2->next, exp->next );

	show( exp );

	return 0;
}



以前寫的錯誤有如下:

1. 必定要把頭指針傳遞進去:

padd( exp1, exp2, exp );



可是,若是exp1,exp2只是用來讀取而不進行修改,能夠傳遞exp1->next進去.



2. 對鏈表的插入,不能這樣操做:
exp = addNode;
exp = exp->next;



在頭部能夠這樣插入,可是在尾部@_@,sorry,空指針會不斷的把exp初始化爲NULL.

因此代碼修改以下(模塊化):

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct POLYNODE{
	int							coef;
	int							expon;
	struct POLYNODE	*next;
}PolyNode;

void insert( PolyNode *exp, PolyNode *addNode )
{
	if ( NULL == exp->next ){
		exp->next = addNode;
	}
	else{
		exp = exp->next;
		while ( NULL != exp->next ){
			exp = exp->next;
		}
		exp->next = addNode;
	}
}
void padd( PolyNode *exp1, PolyNode *exp2, PolyNode *exp )
{
	PolyNode *addNode;

	while ( exp1 && exp2 ){
		addNode = ( PolyNode * )malloc( sizeof( PolyNode ) );
		if ( exp1->expon < exp2->expon ){
			addNode->coef = exp2->coef;
			addNode->expon = exp2->expon;
			addNode->next = NULL;
			insert( exp, addNode );
			exp2 = exp2->next;
			continue;
		}
		else if ( exp1->expon == exp2->expon ){
			if ( 0 == ( exp1->coef + exp2->coef ) ){
				exp1 = exp1->next;
				exp2 = exp2->next;
				continue;
			}
			addNode->coef = exp1->coef + exp2->coef;
			addNode->expon =  exp1->expon;
			addNode->next = NULL;
			insert( exp, addNode );
			
			exp1 = exp1->next;
			exp2 = exp2->next;
			continue;
		}
		else{
			addNode->coef = exp1->coef;
			addNode->expon = exp1->expon;
			addNode->next = NULL;
			insert( exp, addNode );
			
			exp1 = exp1->next;
			continue;
		}
	}

	while ( exp1 ){
		addNode = ( PolyNode * )malloc( sizeof( PolyNode ) );
		addNode->coef = exp1->coef;
		addNode->expon = exp1->expon;
		addNode->next = NULL;
		insert( exp, addNode );
		
		exp1 = exp1->next;
	}

	while ( exp2 ){
		addNode = ( PolyNode * )malloc( sizeof( PolyNode ) );
		addNode->coef = exp2->coef;
		addNode->expon = exp2->expon;
		addNode->next = NULL;
		insert( exp, addNode );
		
		exp2 = exp2->next;
	}
}

void show( PolyNode *exp )
{
	exp = exp->next;
	while ( exp ){
		printf("%d * x^%d + ", exp->coef, exp->expon );
		exp = exp->next;
	}
	printf("0\n");
}

int main( void )
{
	PolyNode *exp1 = ( PolyNode * )malloc( sizeof( PolyNode ) );
	PolyNode *exp2 = ( PolyNode * )malloc( sizeof( PolyNode ) );
	PolyNode *exp = ( PolyNode * )malloc( sizeof( PolyNode ) );
	PolyNode node1_1, node1_2, node1_3, node2_1, node2_2, node2_3;
	exp1->next = NULL;
	exp2->next = NULL;
	exp->next = NULL;

	node1_1.coef = 3;
	node1_1.expon = 14;
	node1_1.next = &node1_2;
	node1_2.coef = 2;
	node1_2.expon = 8;
	node1_2.next = &node1_3;
	node1_3.coef = 1;
	node1_3.expon = 0;
	node1_3.next = NULL;

	node2_1.coef = 8;
	node2_1.expon = 14;
	node2_1.next = &node2_2;
	node2_2.coef = -3;
	node2_2.expon = 10;
	node2_2.next = &node2_3;
	node2_3.coef = 10;
	node2_3.expon = 6;
	node2_3.next = NULL;

	exp1->next = &node1_1;
	exp2->next = &node2_1;

	padd( exp1->next, exp2->next, exp );

	show( exp );

	return 0;
}



程序輸出:

多項式的循環鏈表中,涉及到一個很是有用的概念:內存池.就是分配一塊內存用於存儲和刪除,這樣就沒必要每次malloc了.可是這裏內存池實際上能夠用數組來實現(堆棧),這樣更加的方便.因而本身寫了如下的算法:

PS:這裏爲了簡單的討論,鏈表的插入就直接在頭部插入了,並且並非循環鏈表:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_STACK 100

typedef struct NODE{
	int					data;
	struct NODE	*next;
}Node;

Node stack[ MAX_STACK ];
int top = 0;

void insert( Node *node, int data )
{
	Node *tempnode;
	if ( top == MAX_STACK - 1 ){
		printf("sorry. 木有內存空間了\n");
		return;
	}
	tempnode = &stack[ top++ ];		//這樣作只是爲了top能及時的++
	tempnode->data = data;
	tempnode->next = NULL;
	if ( NULL == node->next ){
		node->next = tempnode;
	}
	else{
		tempnode->next = node->next;
		node->next = tempnode;
	}
}

void show( Node *node )
{
	node = node->next;
	while ( node ){
		printf("%d->", node->data );
		node = node->next;
	}
	printf("NULL\n");
}

int main( void )
{
	Node node = stack[ top++ ];
	node.next = NULL;
	insert( &node, 1 );
	insert( &node, 2 );
	insert( &node, 3 );
	insert( &node, 4 );
	insert( &node, 5 );

	show( &node );

	return 0;
}



程序輸出:

若是你手賤的話,可能會寫出下列的代碼(個人初版本---結果調試的時候發現指針亂竄):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_STACK 100

typedef struct NODE{
	int					data;
	struct NODE	*next;
}Node;

Node stack[ MAX_STACK ];
int top = 0;

void insert( Node *node, int data )
{
	Node tempnode;
	if ( top == MAX_STACK - 1 ){
		printf("sorry. 木有內存空間了\n");
		return;
	}
	tempnode = stack[ top++ ];		//這樣作只是爲了top能及時的++
	tempnode.data = data;
	tempnode.next = NULL;
	if ( NULL == node->next ){
		node->next = &tempnode;
	}
	else{
		tempnode.next = node->next;
		node->next = &tempnode;
	}
}

void show( Node *node )
{
	node = node->next;
	while ( node ){
		printf("%d->", node->data );
		node = node->next;
	}
	printf("NULL\n");
}

int main( void )
{
	Node node = stack[ top++ ];
	node.next = NULL;
	insert( &node, 1 );
	insert( &node, 2 );
	insert( &node, 3 );
	insert( &node, 4 );
	insert( &node, 5 );

	show( &node );

	return 0;
}
你會發現tempnode的地址實際上會指向以前賦值的node,而後tempnode不斷的被更改,就是實際上:
tempnode = stack[ top++ ];

並無達到你想要的效果.


4.5 鏈表的其餘操做

1. 翻轉單鏈表---在上面已經寫過了.

2. 串接單鏈表---把第二個鏈表的頭節點放在第一個鏈表的尾節點便可.

4.6 等價關係

關於等價關係,我並不瞭解書上的那個VLSI的應用場景,因此略過.

4.7 稀疏矩陣

書上用稀疏矩陣的數據結構過於麻煩,直接不想看.在編程的世界裏,有幾點必須注意:1. 儘可能把代碼寫簡單.2.性能沒有你想象中的那麼重要,可維護性在某些方面更加劇要.因此,在作項目的時候,若是性能沒有那麼重要,推薦使用高級語言,不要使用C.C只是用來打基礎的.


4.8 雙向鏈表

最後以一個雙向鏈表的實現來結束本章(這裏只是簡單的實現,並無設計到排序等狀況.鏈表的做用是用於存儲,若是關聯到排序,請使用二叉樹.這裏要用循環鏈表實現,不然你得維護兩張鏈表):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct DLINK{
	int						data;
	struct DLINK		*prev;
	struct DLINK		*next;
}Dlink;

void insertFront( Dlink *dlink, int data );
void insertTail( Dlink *dlink, int data );
void delete( Dlink *dlink, int data );
int search( Dlink *dlink, int data );
void show( Dlink *dlink );

int main( void )
{
	int i = 0;
	Dlink *dlinkHead = ( Dlink * )malloc( sizeof( Dlink ) );
	dlinkHead->next = dlinkHead->prev = NULL;

	for ( i = 0; i < 10; i++ ){
		insertFront( dlinkHead, i );
		insertTail( dlinkHead, i );
	}

	for ( i = 0; i < 10; i += 3 ){
		delete( dlinkHead, i );
	}

	if ( search( dlinkHead, 5 ) ){
		printf("5 in the double link list\n");
	}
	else{
		printf("5 not in the double link list\n");
	}

	if ( search( dlinkHead, 3 ) ){
		printf("3 in the double link list\n");
	}
	else{
		printf("3 not in the double link list\n");
	}

	show( dlinkHead );

	return 0;
}

void insertFront( Dlink *dlink, int data )
{
	Dlink *tempDlink = ( Dlink * )malloc( sizeof( Dlink ) );
	tempDlink->data = data;
	if ( ( NULL == dlink->prev ) && ( NULL == dlink->next ) ){
		dlink->prev = tempDlink;
		dlink->next = tempDlink;
	}
	else{
		dlink->next->prev = tempDlink;
		tempDlink->prev = NULL;			//用於判斷鏈表的頭部
		tempDlink->next = dlink->next;
		dlink->next = tempDlink;
	}
}

void insertTail( Dlink *dlink, int data )
{
	Dlink *tempDlink = ( Dlink * )malloc( sizeof( Dlink ) );
	tempDlink->data = data;
	if ( ( NULL == dlink->prev ) && ( NULL == dlink->next ) ){
		dlink->prev = tempDlink;
		dlink->next = tempDlink;
	}
	else{
		tempDlink->prev = dlink->prev;
		tempDlink->next = NULL;			//用於判斷鏈表的尾部
		dlink->prev->next = tempDlink;
		dlink->prev = tempDlink;
	}
}

void delete( Dlink *dlink, int data )
{
	Dlink *headNode = ( Dlink * )malloc( sizeof( Dlink ) );
	headNode = dlink;		//用於存儲頭節點
	if ( ( NULL == dlink->prev ) && ( NULL == dlink->next ) ){
		return;
	}
	dlink = dlink->next;
	while ( dlink->next ){
		if ( data != dlink->data ){
			dlink = dlink->next;
		}
		else{
			if ( NULL == dlink->prev ){
				headNode->next = dlink->next;
				dlink->prev = NULL;
			}
			else if ( NULL == dlink->next ){
				headNode->prev = dlink->prev;
				dlink->prev->next = NULL;
			}
			else{
				dlink->next->prev = dlink->prev;
				dlink->prev->next = dlink->next;
			}
			dlink = dlink->next;
		}
	}
}
int search( Dlink *dlink, int data )
{
	dlink = dlink->next;
	while ( dlink ){
		if ( data == dlink->data ){
			return 1;
		}
		dlink = dlink->next;
	}

	return 0;
}
void show( Dlink *dlink )
{
	dlink = dlink->next;

	while ( dlink ){
		printf("%d-->", dlink->data );
		dlink = dlink->next;
	}

	printf("NULL\n");
}



程序輸出:

相關文章
相關標籤/搜索