1. 程式人生 > >資料結構02--線性表、單鏈表

資料結構02--線性表、單鏈表

資料型別

資料型別:是指一組性質相同的值的集合及定義在此集合上的一些操作的總稱。

線性表

線性表:由零個或多個數據元素組成的有限序列。

Operation:

  • InitList(*L):初始化操作,建立一個空的線性表L。
  • ListEmpty(L):判斷線性表是否為空表,若線性表為空表,返回true,否則返回false。
  • CLearList(*L):將線性表清空。
  • GetElem(L,i,e):將線性表L中的第i個位置元素值返回給e。
  • LocateElem(L,e):線上性表L中查詢與給定值e相等的元素,如果查詢成功,返回該元素在表中序號表示成功,否則,返回0表示失敗。
  • ListInsert(*L,i,e):線上性表L中第i個位置插入新元素e。
  • ListDelete(*L,i,*e):刪除線性表L中第i個位置元素,並用e返回其值。
  • ListLength(L):返回線性表L的元素個數。
線性表的順序儲存結構

線性表的順序儲存結構,在存、讀資料時,不管是哪個位置,時間複雜度都是O(1)。而在插入或刪除時,時間複雜度都是O(n)。

優缺點:

優點:

  • 無需為表示表中元素之間的邏輯關係而增加額外的儲存空間;
  • 可以快速的存取表中任意位置的元素。

缺點:

  • 插入和刪除操作需要移動大量的元素;
  • 當線性表長度變化較大時,難以確定儲存空間的容量;
  • 容易造成儲存空間的“碎片”。
線性表的鏈式儲存結構

線性表的鏈式儲存結構的特點是用一組任意的儲存單元儲存線性表的資料元素,這組儲存單元可以存在記憶體中未被佔用的任意位置。

比起順序儲存結構每個元素只需要儲存一個位置就可以了。現在鏈式儲存結構中,除了要儲存資料元素資訊外,還要儲存它的後繼元素的儲存地址(指標)。

也就是說除了儲存其本身的資訊外,還需儲存一個指示其直接後繼的儲存位置的資訊。

我們把儲存資料元素資訊的域稱為資料域,把儲存直接後繼位置的域稱為指標域。指標域中儲存的資訊稱為指標或鏈。這兩部分資訊組成資料元素稱為儲存映像,稱為結點(Node)。

n個結點連結成一個連結串列,即為線性表(a1,a2,a3,…,an)的鏈式儲存結構。

因為此連結串列的每個結點中只包含一個指標域,所以叫做單鏈表。

單鏈表

對於線性表來說,總得有個頭有個尾,連結串列也不例外。我們把連結串列中的第一個結點的儲存位置叫做頭指標,最後一個結點指標為空(NULL)。

頭指標與頭結點的異同

頭指標
  • 頭指標是指連結串列指向第一個結點的指標,若連結串列有頭結點,則是指向頭結點的指標;
  • 頭指標具有標識作用,所以常用頭指標冠以連結串列的名字(指標變數的名字);
  • 無論連結串列是否為空,頭指標均不為空;
  • 頭指標是連結串列的必要元素。
頭結點
  • 頭結點是為了操作的統一和方便而設立的,放在第一個元素的結點之前,其資料域一般無意義(但也可以用來存放連結串列的長度);
  • 有了頭結點,對在第一元素結點前插入結點和刪除第一結點,其操作與其他結點的操作就統一了;
  • 頭結點不一定是連結串列的必須要素。
單鏈表的插入
  • s->next = p->next;
  • p->next = s;

單鏈表第i個數據插入結點的演算法思路:

  • 宣告一結點p指向連結串列頭結點,初始化j從1開始;
  • 當j<1時,就遍歷連結串列,讓p的指標向後移動,不斷指向下一結點,j累加1;
  • 若到連結串列末尾p為空,則說明第i個元素不存在;
  • 否則查詢成功,在系統中生成一個空結點s;
  • 將資料元素e賦值給s->data;
  • 單鏈表的插入剛才兩個標準語句;
  • 返回成功;
單鏈表的刪除
  • p->next = p->next->next;

單鏈表第i個數據刪除結的演算法思路:

  • 宣告結點p指向連結串列第一個結點,初始化j=1;
  • 當j<1時,就遍歷連結串列,讓p的指標向後移動,不斷指向下一個節點,j累加1;
  • 若到連結串列末尾p為空,則說明第i個元素不存在;
  • 否則查詢成功,將欲刪除結點p->next賦值給q;
  • 單鏈表的刪除標準語句p->next = q->next;
  • 將q結點中的資料賦值給e,作為返回;
  • 釋放q結點;

程式碼如下:

Status ListDelete(LinkList *L, int i, ElemType *e )
{
	int j;
	LinkList p,q;
	p = *L;
	j = 1;
	while( p->next && j<i )
	{
		p = p->next;
		++j;
	}
	if( !(p->next) || j>i )
	{
		return ERROR;
	}
	q = p->next;
	p->next = q-<next;
	*e = q->data;
	free(q);
	return ok;
}
單鏈表整表建立的演算法思路如下:
  • 宣告一結點p和計數器變數i;
  • 初始化一空連結串列L;
  • 讓L的頭結點的指標指向NULL,即建立一個帶頭結點的單鏈表;
  • 迴圈實現後繼結點的賦值和插入。

頭插法建立單鏈表:

頭插法從一個空表開始,生成新結點,讀取資料存放到新結點的資料域中,然後將新結點插入到當前連結串列的表頭上,直到結束為止。

簡單來說,就是把新加進的元素放在表頭後的第一個位置:

  • 先讓新結點的next指向頭結點之後;
  • 然後讓表頭的next指向新結點;
單鏈表整表刪除的演算法思路如下:
  • 宣告結點p和q;
  • 將第一個結點賦值給p,下一結點賦值給q;
  • 迴圈執行釋放p和將q賦值給p的操作;

程式碼如下:

Status ClearList(LinkList *L)
{
	LinkList p,q;
	p = (*L)->next;
	while (p):
	{
		q = p->next;
		free(p);
		p = q;
	}
	(*L)->next = NULL;
	return ok;
}
單鏈表結構與順序儲存結構優缺點

儲存分配方式:

  • 順序儲存結構用一段連續的儲存單元依次儲存線性表的資料元素;
  • 單鏈表採用鏈式儲存結構,用一組任意的儲存單元存放線性表元素;

時間效能:

  • 查詢:

    順序儲存結構O(1);
    單鏈表O(n);

  • 插入和刪除:

    順序儲存結構需要平均移動表長一半的元素,時間複雜度為O(n);
    單鏈表在計算出某位置的指標後,插入和刪除時間複雜度僅為O(1);

空間效能:

  • 順序儲存結構需要預分配儲存空間,分大了,容易造成空間浪費,分小了,容易發生溢位;
  • 單鏈表不需要分配儲存空間,只要有就可以分配,元素個數也不受限制;

綜上所述:

  • 若線性表需要頻繁查詢,很少進行插入和刪除操作時,宜採用順序儲存結構;
  • 若需要頻繁插入和刪除時,宜採用單鏈表結構;