1. 程式人生 > >c語言實現單鏈表資料結構及其基本操作

c語言實現單鏈表資料結構及其基本操作

 帶頭結點單鏈表。分三個檔案,分別為標頭檔案,測試檔案,原始碼檔案。

給出了單鏈表的部分操作,每個操作都有對應的註釋說明該函式功能。

test.c  檔案中,對輸入資料是否合法進行了檢測,但是並未實現輸入資料不合法時應該採取的措施。

測試檔案  通過一個選單來測試單鏈表功能。

list.h:

/*  單鏈表型別的標頭檔案  */  


#ifndef LIST_H_
#define LIST_H_ 


struct Node;									//先宣告,就可以在下面使用,而不必因定義在後面而產生衝突

 
/*  一般型別定義  */  
typedef int ElementType;                        //抽象資料型別  
typedef struct Node * PtrToNode;
typedef PtrToNode List;
typedef PtrToNode Position;


/*  特定於程式的宣告  */   
struct Node {
    ElementType element;  
	Position	next;
};


/*  函式原型  */  
List MakeEmpty ( List l );
int IsEmpty ( List L );
int IsLast ( Position p, List l );
Position Find ( ElementType x, List l );
void Delete ( ElementType x, List l );
Position FindPrevious ( ElementType x, List l );
void Insert ( ElementType x, List l, Position p );
void DeleteList ( List l );
Position Header ( List l );
Position First ( List l );
Position Last ( List l );
Position Advance ( Position p );
ElementType Retrieve ( Position p );
void PushFront ( ElementType x, List l );
void PopFront ( List l );
void PushBack ( ElementType x, List l );
void PopBack ( List l );


#endif

test.c:
/*  單鏈表  */  


#include <stdio.h>  
#include <windows.h>  
#include "list.h"


struct Node;


void Menu ( void );								//選單  
void PrintList ( List l );						//列印單鏈表


int main ( void )  
{  

    List l;
	int choice;  
	ElementType x;  

	l = NULL;
	choice = 0;
	x = 0;
  
    Menu (  );
  
    scanf ( "%d", &choice );  
  
    while ( 0 != choice )  
    {  
        switch ( choice )  
        {  
        case 1: if ( NULL == ( l = MakeEmpty ( l ) ) )	//如若不接受返回值,則外面 l 未改變。因為 MakeEmpty 中形參 不影響 實參值,開闢的空間反而找不到了!重點!!
				{
				printf ( "初始化失敗!\n" );
				}
				else
				{
					printf ( "初始化成功!\n" );
				}
                break;  
        case 2: if ( 1 == IsEmpty ( l ) )
				{
					printf ( "單鏈表為空!\n" );
				}
				else
				{
					printf ( "單鏈表不為空!\n" );
				}
                break;  
		case 3: {
					Position p;

					p = Last ( l );

					if ( NULL == p )
					{
						printf ( "0.連結串列中沒有結點,當前位置是連結串列末尾!\n" );

						break;
					}

					if ( 1 == IsLast ( p , l ) )
					{
						printf ( "1.當前位置是連結串列末尾!\n" );
					}

					if ( 0 == IsLast ( l, l ) )
					{
						printf ( "2.當前位置不是連結串列末尾!\n" );
					}
  
				  break;  
				}
		case 4:	printf ( "請輸入需要查詢的元素: " );

				if ( 1 == scanf ( "%d", &x ) )
				{
					Position tmp;

					tmp = Find ( x ,l );

					if ( NULL == tmp )
					{
						printf ( "元素%d不存在!\n", x );
					}
					else
					{
						printf ( "找到了元素%d!\n", ( Retrieve ( tmp ) ) );
					}
				}
				else
				{
					printf ( "輸入資料不合法,查詢失敗!\n" );
				}

				break;
        case 5: printf ( "請輸入需要刪除的元素: " );

				if ( 1 == scanf ( "%d", &x ) )
				{
					Delete ( x, l );

					printf ( "刪除成功!\n" );
				}
				else
				{
					printf ( "輸入資料不合法,刪除失敗!\n" );
				}

				break;
        case 6: printf ( "請輸入需要插入的元素: " );//此處實現為頭插

				if ( 1 == scanf ( "%d", &x ) )
				{
					Insert ( x, l, l );

					printf ( "插入成功!\n" );
				}
				else
				{
					printf ( "輸入資料不合法,插入失敗!\n" );
				}

                break;  
        case 7: DeleteList ( l );

				printf ( "刪除單鏈表成功!\n" );

				l = NULL;

				break;
        case 8: printf ( "請輸入需要插入的元素: " );

				if ( 1 == scanf ( "%d", &x ) )
				{
					PushFront ( x, l );

					printf ( "插入成功!\n" );
				}
				else
				{
					printf ( "輸入資料不合法,插入失敗!\n" );
				}

                break;  
        case 9: PopFront ( l );
			
				printf ( "刪除成功!\n" );

                break;  
        case 10: printf ( "請輸入需要插入的元素: " );

				 if ( 1 == scanf ( "%d", &x ) )
				 {
					 PushBack ( x, l );

				 	 printf ( "插入成功!\n" );
				 }
				 else
				 {
					 printf ( "輸入資料不合法,插入失敗!\n" );
			 	 }

                 break;  
        case 11: PopBack ( l );

				 printf ( "刪除成功!\n" );

                 break;  
        case 12: PrintList ( l );

				 break;
        default: printf ( "放棄吧!\n" );   
                   
                 break;  
        }     
  
        Menu ( );  
        scanf ( "%d", &choice );  
    }  
  
    system ( "pause" );  
  
    return 0;  
  
}  
  
  
void Menu ( void )                              //選單  
{  
    printf ( "***************************************************************\n" );  
    printf ( "  1.初始化					2.是否為空\n" );  
    printf ( "  3.當前位置是否是連結串列末尾			4.查詢元素\n" );  
    printf ( "  5.刪除元素					6.插入元素\n" );  
    printf ( "  7.刪除連結串列					8.頭插資料\n" );  
    printf ( "  9.頭刪資料					10.尾插資料\n" );  
    printf ( "  11.尾刪資料					12.列印\n" );  
    printf ( "***************************************************************\n" );  
}  
  
void PrintList ( List l )						//列印單鏈表
{  
	Position p;

	p = l -> next;

	while ( NULL != p )
	{
		printf ( "%d -> ", ( p -> element ) );

		p = p -> next;
	}

	printf ( "\n" );
}  
  
list.c:
/*  list.c  -- 支援單鏈表操作的函式  */  


#include <stdio.h>  
#include <stdlib.h>
#include "list.h"  


/*  介面函式  */  


/*  把單鏈表初始化為空單鏈表  */				//必須先初始化再使用連結串列(未初始化連結串列下列函式可能出錯.)
List MakeEmpty ( List l )						//傳進來的 l 是一個指向單鏈表這種資料結構的指標,但不一定已經開闢了這種結構的空間使指標去指向
{
	if ( NULL != l )							//結合 收藏夾 -> c語言,  理解為什麼將指標free( )後需置NULL
	{
		DeleteList ( l );
	}

	l = ( List )malloc ( sizeof ( struct Node ) );//malloc 完一定要檢查是否 開闢空間成功!

	if ( NULL == l )
	{
		printf ( "開闢空間失敗!" );
		return NULL;
	}

	l -> element = 0;
	l -> next = NULL;

	return l;
}

/*  檢查單鏈表是否為空  */  
int IsEmpty ( List l )  
{  
	return ( NULL == l -> next );
}  
 
/*  檢查當前位置是否是連結串列的末尾  */			//將只有頭結點情況視為連結串列末尾
int IsLast ( Position p, List l )
{
	return ( NULL == p -> next );
}

/*  在單鏈表中查詢元素x  */
Position Find ( ElementType x, List l )
{
	Position p;

	p = l -> next;								//宣告和定義分開.

	while ( ( NULL != p ) && ( x != p -> element ) )
	{
		p = p -> next;
	}

	return p;
}

/*  在單鏈表中刪除元素x  */						//元素不存在,函式不做任何處理
void Delete ( ElementType x, List l )			//使用到了 FindPrevious( )函式
{
	Position p;
	Position tmpCell;
	
	p = FindPrevious ( x, l );

	if ( !( IsLast ( p, l ) ) )
	{
		tmpCell = p -> next;
		p -> next = p -> next -> next;			//注意!是賦值給p的next域,而不是p!	
		free ( tmpCell );						//free( )掉指標後,最好將指標置NULL.
		tmpCell = NULL;
	}
}

/*  在單鏈表中找出元素x的前驅位置  */			//輔助Find函式實現,不提供測試程式碼(若Find函式執行成功,則此函式正確)
Position FindPrevious ( ElementType x, List l )
{
	Position p;

	p = l;										//在外面申請頭節點,所以l不可能為空

	while ( ( NULL != p -> next ) && ( x != p -> next -> element ) )
	{
		p = p -> next;
	}

	return p;									//返回的不是next域!也永遠不可能是!
}

/*  在單鏈表中插入元素x  */
/*  插入到當前位置的後面  */
void Insert ( ElementType x, List l, Position p )
{
	Position tmpCell;							//只是建立了一個指標,還需要malloc出結構體的大小.5.

	tmpCell = ( List )malloc ( sizeof ( struct Node ) );//struct Node 只是一個型別, sizeof( sturct Node )才是它的大小. 才能使用malloc.

	if ( NULL == tmpCell )						//因為系統資源問題,所以malloc申請空間可能會失敗,養成malloc後檢查申請是否成功的習慣6.
	{
		printf ( "申請空間失敗" );
	}
	else
	{
	tmpCell -> element = x;
	tmpCell -> next = p -> next;
	p -> next = tmpCell;
	}
}

/*  刪除單鏈表  */
void DeleteList ( List l )						//刪除單鏈表後再使用單鏈表必須先初始化.
{
	Position p;
	Position tmp;

	p = l -> next;								//l 的next 域存放地址給p  free l 後,這個p所儲存地址依然在和 l 無關。 lnext域只是之前用來儲存它
	free ( l );									//此處通過指標l釋放l指向的頭結點,而不是釋放指標l,指標l依然存在.
	l = NULL;									//記得,你在此處改變l的值,外面l值依舊沒改變。   所以,這種錯誤不要再犯了!  

	while ( NULL != p )
	{
		tmp = p -> next;	
		free ( p );								//此處釋放的是p,不是p的next域(不要釋放下一個節點)
		p = tmp;
	}
}

/*  返回連結串列頭結點  */							//不提供測試用例
Position Header ( List l )
{
	return l;
}

/*  返回連結串列第一個結點  */						//不提供測試用例
Position First ( List l )
{
	return ( l -> next );
}

/*  返回連結串列的最後一個結點  */					//不提供測試用例
Position Last ( List l )
{
	Position p = l;

	if ( NULL == l -> next )					//將頭節點後那個結點視為第一個結點,所以只有頭結點時,視為沒有結點.
	{
		return NULL;
	}
	else
	{
		while ( NULL != p -> next )
		{
			p = p -> next;
		}
	}

	return p;
}

/*  返回位置p的下一個位置  */					//不提供測試用例
Position Advance ( Position p )
{
	return ( p -> next ); 
}

/*  返回位置p處的元素  */						//不提供測試用例
ElementType Retrieve ( Position p )
{
	return ( p -> element );
}

/////*  在單鏈表頭部插入元素x  */					//版本1
//void PushFront ( ElementType x, List l )		//使用到了 Header( )函式 與 Insert( )函式
//{
//	Insert ( x, l, Header ( l ) );
//}

/*  在單鏈表頭部插入元素x  */					//版本2
void PushFront ( ElementType x, List l )
{
	Position tmp;								//不需要再定義一個多餘的變數儲存 l -> next.

	tmp = ( List )malloc ( sizeof ( struct Node ) );

	if ( NULL == tmp )
	{
		printf ( "申請空間失敗,插入不成功!" );//抽象資料型別中別加多餘東西( 換行符 ).
	}
	else
	{
		tmp -> element = x;
		tmp -> next = l -> next;
		l -> next = tmp;
	}
}

/*  刪除單鏈表第一個結點  */
void PopFront ( List l )		
{
	if ( NULL == l -> next )
	{
		;
	}
	else
	{
		Position tmp;

		tmp = l -> next;						//注意此處釋放 l的next域中值才是釋放了第一個結點,而不是釋放第一個結點next域(第二個結點).
		l -> next = tmp -> next;
		
		free ( tmp );							//釋放的next域只是下一個結點,不是這個結點,也不是釋放了next.
		tmp = NULL;
	}
}

/*  在單鏈表尾部插入元素x  */
void PushBack ( ElementType x, List l )			//使用 Header( )函式
{
	Position p;
	Position tmp;

	p = Header ( l );

	while ( NULL != p -> next )
	{
		p = p -> next;
	}

	tmp = ( List )malloc ( sizeof ( struct Node ) );

	if ( NULL == tmp )
	{
		printf ( "申請空間失敗,插入失敗" );
	}
	else
	{
		tmp -> element = x;
		tmp -> next = NULL;
		p -> next = tmp;
	}
}

/*  刪除單鏈表最後一個結點  */
void PopBack ( List l )
{
	if ( NULL == l -> next )
	{
		;
	}
	else
	{
		Position p;

		p = l;

		while ( NULL != p -> next -> next  )
		{
			p = p -> next;
		}

		free ( p -> next );
		p -> next = NULL;
	}
}