重刷資料結構,小題大做,——難道非要頭結點嗎?
阿新 • • 發佈:2019-01-06
- 由於開始結點的位置被存放在頭結點的指標域中,所以在連結串列的第一個位置上的操作和在表的其他位置上的操作一致,無須進行特殊處理。
- 無論連結串列是否為空,其頭指標是指向頭結點的非空指標,因此空表和非空表的處理也就一致了。
s->next = p->next; p->next = s; 刪除結點
p->next = q->next; free(q); 從上面可以看出,插入和刪除結點的核心操作在於改變結點的next指標域指向。而上面都是通過指向結點的指標來改變結點的next域的,比如p->next = q->next。在沒有頭結點的情況下,如果要插入和刪除第一個結點,需要修改頭指標。而頭指標不屬於任何結點,不能通過修改某個結點的next指標域來修改頭指標,所以得特殊處理,這就帶來了操作的不一致性。頭指標、結點的next指標域,它們雖然都是同一種類型的指標,但得分開處理。 所以問題的來源在於:通過指向結點的指標來訪問結點的next指標域,這種方法不適合頭指標。
書上的解決方法
新的解決方法 前面說了,問題的來源是“通過指向結點的指標來訪問結點的next指標域,這種方法不適合頭指標” 。這種訪問方法把頭指標和結點的next指標當做兩個不同的東西。那我們為什麼不把頭指標和結點的next指標域看做同一種東西呢?它們本就是同樣的資料型別啊!這樣不是可以一致處理了。把它們都看作指向結點的指標,就可以通過指向指標的指標來修改和訪問了。新的演算法如下: 插入
s->next = *p; //p指向指標的指標 *p = s; 刪除
*p = q->next; //p指向指標的指標 free(q); 小弟通過上面的思路寫程式碼證實了,這種方法可以達到同樣的效果。不需要新增頭結點,而且操作一致。 程式碼 下面是兩份程式碼,一份是教材上有頭結點的版本,一份是本文提出的無頭結點的版本。程式碼都通過測試!
/*
*有頭結點版本
*作者:善良超哥哥
*時間:2014-8-16
*/
//LinkList.h
#ifndef _LIMKLIST_H_
#define _LINKLIST_H_
typedef struct LNode{
char data;
struct LNode *next;
}LNode;
typedef struct{
LNode *head; //指向第一個結點的指標
int len; //連結串列長度
}LinkList;
//建立連結串列,尾插法
void CreateList(LinkList *L);
//在第i個位置插入元素e
int ListInsert(LinkList *L,int i,char e);
//刪除第i個位置的元素,並用*e返回
int ListDelete(LinkList *L,int i,char *e);
//遍歷列印連結串列中的所有元素
void ListPrint(LinkList *L);
#endif
//LinkList.c
/*
有頭結點的連結串列實現
*/
#include "LinkList.h"
#include <stdio.h>
#include <stdlib.h>
//建立連結串列,尾插法
void CreateList(LinkList *L)
{
L->head = malloc(sizeof(LNode));//建立頭結點
L->head->next = NULL;
L->len = 0;
//開始輸入字元,建立連結串列,以#結束
char c;
LNode *p = L->head;
while((c = getchar()) != '#')
{
LNode *s = malloc(sizeof(LNode));
s->data = c;
p->next = s;
p = s;
++(L->len);
}
p->next = NULL;
}
//在第i個位置插入元素e
int ListInsert(LinkList *L,int i,char e)
{
if(i < 1 || i > L->len || L == NULL)
{
return 0;//失敗
}
LNode *p = L->head;
for(int j = 1; j < i; j++)
{
p = p->next;
}
LNode *s = malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
++(L->len);
return 1;
}
//刪除第i個位置的元素,並用*返回
int ListDelete(LinkList *L,int i,char *e)
{
if(L == NULL || i < 1 || i >= L->len)
{
return 0;//失敗了
}
LNode *p = L->head;
for(int j = 1; j < i; j++)
{
p = p->next;
}
//刪除p指標指向的結點的下一個結點
LNode *q = p->next;
p->next = q->next;
free(q);
--(L->len);
return 1;
}
//遍歷列印連結串列中的所有元素
void ListPrint(LinkList *L)
{
LNode *p = L->head->next;
while(p!=NULL)
{
printf("%c",p->data);
p = p->next;
}
printf("\n");
}
無頭結點版本:/*
*無頭結點版本
*作者:善良超哥哥
*時間:2014-8-16
*/
//LinkList.h
#ifndef _LIMKLIST_H_
#define _LINKLIST_H_
typedef struct LNode{
char data;
struct LNode *next;
}LNode;
typedef struct{
LNode *head; //指向第一個結點的指標
int len; //連結串列長度
}LinkList;
//建立連結串列,尾插法
void CreateList(LinkList *L);
//在第i個位置插入元素e
int ListInsert(LinkList *L,int i,char e);
//刪除第i個位置的元素,並用*e返回
int ListDelete(LinkList *L,int i,char *e);
//遍歷列印連結串列中的所有元素
void ListPrint(LinkList *L);
#endif
//LinkList.c #include "LinkList.h" #include <stdio.h> #include <stdlib.h> //建立連結串列,尾插法 void CreateList(LinkList *L) { L->head = NULL; L->len = 0; //開始輸入字元,建立連結串列,以#結束 char c; LNode **p = &(L->head); while((c = getchar()) != '#') { LNode *s = malloc(sizeof(LNode)); s->data = c; (*p) = s; p = &(s->next); ++(L->len); } (*p) = NULL; } //在第i個位置插入元素e int ListInsert(LinkList *L,int i,char e) { if(i < 1 || i > L->len || L == NULL) { return 0;//失敗 } LNode **p = &(L->head); for(int j = 1; j < i; j++) { p = &((*p)->next); } LNode *s = malloc(sizeof(LNode)); s->data = e; s->next = *p; *p = s; ++(L->len); return 1; } //刪除第i個位置的元素,並用*返回 int ListDelete(LinkList *L,int i,char *e) { if(L == NULL || i < 1 || i >= L->len) { return 0;//失敗了 } LNode **p = &(L->head); for(int j = 1; j < i; j++) { p = &((*p)->next); } //刪除p指標指向的結點 LNode *q = *p; *p = q->next; *e = q->data; free(q); --(L->len); return 1; } //遍歷列印連結串列中的所有元素 void ListPrint(LinkList *L) { LNode *p = L->head; while(p!=NULL) { printf("%c",p->data); p = p->next; } printf("\n"); }
演算法對比