資料結構之連結串列(Linked-List)及操作
一、 資料結構之連結串列(Linked-List)
線性表是最常用的儲存結構,線性表的每個單元稱為元素,元素擁有一個數據及一個地址線性表有兩種物理儲存方式:順序儲存方式和鏈式儲存方式陣列是具有代表性的順序儲存方式的線性表,單鏈表是具有代表性的鏈式儲存方式的線性表。
1.陣列
陣列的記憶體是連續分配的,可以通過陣列的索引直接獲取對應的資料,其索引就是線性表中所說的元素的“地址”,但索引不是地址。
2.單鏈表
單鏈表的記憶體的是鏈式的,也就是不連續的,元素中的地址指向連結串列中下一個元素的地址。
關於單鏈表的頭指標和頭結點
頭指標不是頭結點。頭指標指向頭結點,而頭結點通常data域為NULL或者為單鏈表的length,在如下資料結構中,
typedef struct LinkedListNode
{//定義單鏈表
int value;
struct LinkedListNode *next;
}*LinkedList;
假如定義
LinkedList L,則L是我們需要的頭指標(頭指標在單鏈表是必須的而頭結點不是),
又再L=(LinkedList malloc (sizeof(LinkedListNode)); 這就申請了一個結點,這裡第一個結點也就是頭結點。
定義單鏈表
單鏈表有兩個域,data域用於存放資料,next域用於存放下一個結點的地址。
typedef struct LinkedListNode
{//定義單鏈表
int value;
struct LinkedListNode *next;
}*LinkedList;
建立單鏈表
LinkedList CreateLinkedList()
{
LinkedListNode* newNode = (LinkedListNode*)malloc(sizeof(LinkedListNode));
if (newNode != NULL)
{
newNode->value = 0;
newNode->next = NULL;
return newNode;
}
else
{
return NULL;
}
}
建立新結點
LinkedListNode* CreateLinkedListNode(int val)
{//創造新的單鏈表結點
LinkedListNode* newNode = (LinkedListNode*)malloc(sizeof(LinkedListNode));
if (newNode != NULL)
{
newNode -> value = val;
newNode->next = NULL;
return newNode;
}
else
{
return NULL;
}
}
前插法插入新元素(新結點)
前插:在頭結點後面插入新元素,則頭結點後面的元素永遠是新元素,設連結串列頭結點head,元素x1,x2,x3按序插入,設連結串列頭尾走向從左到右
(1)Insert後:head x1
(2)Insert後:head x2 x1
(3)Insert後:head x3 x2 x1
void anteriorInsert(LinkedListNode* head, int val)
{
LinkedListNode* newNode = CreateLinkedListNode(val);
newNode->next = head->next;//先使新元素的指標域next指向頭結點的next
head->next = newNode;//再使頭結點的next指向新元素
head->value++; //可以用頭結點的data域儲存連結串列的length
}
後插法插入新元素(新結點)
後插:在尾指標所指向的結點後面插入新元素,尾指標指向的結點永遠是新元素,設元素x1,x2,x3按序插入,設連結串列頭尾走向從左到右。多設定一個尾指標使操作更方便,否則需要遍歷連結串列。
(1)Insert後:x1
(2)Insert後:x1 x2
(3)Insert後:x1 x2 x3
void posteriorInsert(LinkedListNode* head, LinkedListNode* tail, int val)
{
LinkedListNode* newNode = CreateLinkedListNode(val);
tail->next = newNode;//先使尾指標的next指向新元素
tail = tail->next;//再使尾指標指向新元素
head->value++; //可以用頭結點的data域儲存連結串列的length
}
按data域的值查詢結點
這裡找是連結串列中找到第一個data域等於val的結點,不是所有結點
LinkedListNode* SearchByVal(LinkedList L, int val)
{
LinkedListNode* Node = L->next;
while (Node)
{
if (Node->value == val)
{
//找到馬上return
return Node;
}
Node = Node->next;
}
return Node;
}
按data域的值修改結點的data域的值
void UpdateVal(LinkedList L, int preVal , int aftVal)
{//將所有data域等於preVal的結點的data域值修改為aftVal
LinkedListNode* Node = L->next;
while (Node)
{
if (Node->value == preVal)
{
Node->value = aftVal;
}
Node = Node->next;
}
}
按data域的值刪除結點
這裡是找到第一個data域等於val的結點將其刪除
void DeleteByVal(LinkedListNode* head, int val)
{
if (head->next == NULL)
{
cout << "LinkedList is null" << endl;
}
else
{
//從頭結點開始遍歷
LinkedListNode* previousNode = head, *currentNode = head->next;
while (currentNode)
{
if (currentNode->value == val)
{
//刪除目標結點
previousNode->next = currentNode->next;
free(currentNode);
head->value--;
break;
}
else
{
currentNode = currentNode->next;
previousNode = previousNode->next;
}
}
}
return;
}
清空連結串列
void FreeLinkedList(LinkedList L)
{
for (LinkedListNode* temp = L; L!= NULL; temp = L)
{
L = L->next;
free(temp);
}
}
關於單鏈表後插法的疑問
既然後插程式碼如上,那麼應該如何遍歷,從尾指標往前遍歷嗎,那感覺頭指標有點楞。從頭結點往後遍歷,但是如上程式碼的頭結點的next為NULL,於是尷尬。求大神解答。