1 这里我重点总结一下带头结点的单向链表,说实在话,这个带头结点这句话算是迷惑了我很长时间,我们可以把它理解成为结点,也可以不把它理解为结点。只不过是我们定义了一个结构体而已,里面包含了整个链表的属性信息,及一个头指针和一个尾指针。头指针指向第一个结点,尾指针指向最后一个结点。
其中链表的属性信息里面可以包含有链表的长度。当然我们里面可以什么都不存储。下面我们来看一下单向链表在内存中的存储。
|
|
|
由上面例子我们可以很清楚的明白单向链表和顺序链表的区别。即单向链表在内存中是离散存储(非顺序存储)的。这个特点可是说是单链表的一个优点,因为他这种特性使它的插入,删除操作变得异常方便。即只需改变指针的指向就OK,无需移动元素的位置。然而这一优点也给它带来了一个缺点。即无法实现对数据的直接存储。
单链表在定义的时候有两种形式,即我们可以定义一个带头结点的的单链表,也可以定义一个不带头结点的单链表.他们在很多操作方面都有区别的,不止在头结点的问题上。如:
1 带头结点的链表在插入元素的时候我们无需对插入第一个位置考虑很多,它完全可以像普通的插入一样操作,而无头结点的链表就不同了,因为必须的考虑头指针的变动。删除操作亦是如此。
2 初始化带头结点的链表时,我们只需定义一个头结点变量,然后对其里面的元素初始化即可,而无头结点的链表需定义一个节点指针,并对其进行初始化。
3 带头结点的链表在管理时比较方便。
…
下面我们开始定义我们的单链表:
这里我们改变一下风格,即不再采用严老师那种用参数返回值的风格,我们采用直接用函数返回值来返回我们所需要的值,感觉这样更简便,容易理解和接受.
定义结点:
不带头结点:
typedef struct LNode
{
int elem;
struct LNode *next;
}LNode,*Link;
带头结点:
typedef struct LNode
{
int elem;
struct LNode *next;
}LNode,*Link;
typedef struct LinkList
{
Link head,tail;
int length;
}LinkList;
初始化:
不带头结点:
void InitList(Link *L)
{
L = NULL;
}
解释一下这里为什么用了二维指针呢。因为我们在定义不带头结点的单链表的时候肯定是这样的:Link L;即定义了一个节点指针。若我们不用单链表,是无法改变指针的值的,他会想传值一样,在我们传递参数的时候会产生一个副本,这样我们便无法初始化我们的链表了.
带头结点:
void InitList(LinkList *L)
{
L->head = L->tail = NULL;
L->length = 0;
}
销毁链表:
不带头结点:
void DestroyList(Link *L)
{
Link p = *L;
while (p != NULL)
{
(*L) = (*L)->next;
free(p);
p = *L;
}
}
带头结点:
void DestroyList(LinkList *L)
{
Link p = L->head;
while (p != NULL)
{
L->head = L->head->next;
free(p);
p = L->head;
}
}
清空链表:
不带头结点的:
它和销毁一样.
带头结点的:
它和销毁一样.
判断链表为空:
不带头结点的:
int ListEmpty(Link L)
{
if (L == NULL)
{
return 1;
}
return 0;
}
带头结点的:
int ListEmpty(LinkList L)
{
if (L.head == NULL)
{
return 1;
}
return 0;
}
获得链表长度:
不带头结点的:
int ListLength(Link L)
{
int len = 0;
Link p = L;
while (p != NULL)
{
++len;
p = p->next;
}
return len;
}
带头结点的:
int ListLength(LinkList L)
{
return L.length;
}
获得链表指定位置的长度:
不带头结点的:
int GetElem(Link L,int i)
{
int j = 0;
Link p = L;
while (p != NULL && j < i)
{
++j;
p = p->next;
}
if (p == NULL)
{
printf("ilegle position %d(i)./n",i);
return -1;
}
return p->elem;
}
带头结点的:
int GetElem(LinkList L, int i)
{
Link p = L.head;
int j = 0;
while (p != NULL && j < i)
{
++j;
p = p->next;
}
if (p == NULL)
{
printf("ilegle position %d(i)/n",i);
return -1;
}
return p->elem;
}
插入:
不带头结点:
void ListInsert(Link *L,int i,int e)
{
int j = 1;
Link p,q;
if (L == NULL)
{
*L = p;
p->next = NULL;
return;
}
q = *L;
while (q != NULL && j < i)
{
++j;
q = q->next;
}
if (q == NULL)
{
printf("ilegle position %d(i)./n",i);
return;
}
p = (Link)malloc(sizeof(LNode));
p->elem = e;
p->next = q->next;
q->next = p;
}
带头结点:
int ListLength(Link L)
{
int len = 0;
Link p = L;
while (p != NULL)
{
++len;
p = p->next;
}
return len;
}
void ListInsert(LinkList *L,int i,int e)
{
Link p,q;
int j = 1;
if (i <= 0 || i > L->length+1)
{
printf("ilegle position %d(i)",i);
return;
}
p = (Link)malloc(sizeof(LNode));
p->elem = e;
q = L->head;
if (L->length == 0) //如果链表为空.
{
L->head = L->tail = p;
p->next = NULL;
++L->length;
return;
}
if (i == L->length+1)//在最后插入结点.
{
L->tail->next = p;
L->tail = p;
++L->length;
return;
}
while (j < i)
{
++j;
q = q->next;
}
q->next = p->next;
p->next = q;
++L->length;
}
删除操作;
不带头结点的:
void ListDelete(Link *L,int i)
{
Link p,q;
int j = 1;
if ((*L) == NULL)
{
printf("ListDelete cannot operate on empty list./n");
return;
}
if (i == 1)
{
q = (*L);
*L = (*L)->next;
free(q);
return;
}
p = *L;
while (p != NULL && j < i)
{
++j;
p = p->next;
}
if (p == NULL)
{
printf("ilegle position %d(i)/n",i);
return;
}
q = p->next;
p->next = q->next;
free(q);
}
带头结点的:
void ListDelete(LinkList *L,int i)
{
int j;
Link p,q;
if (L->head == NULL)
{
printf("ListDelete cannot operate on empty list./n");
return;
}
if (i <= 0 || i > L->length)
{
printf("ilegle position %d(i)",i);
return;
}
p = L->head;
if (i == 1)
{
q = p->next;
p->next = q->next;
L->head = p->next;
free(q);
--L->length;
return;
}
j = 1;
while (j < i)
{
++j;
p = p->next;
}
if (i == L->length)
{
q = p->next;
p->next = NULL;
L->tail = p;
free(q);
--L->length;
return;
}
q = p->next;
p->next = q->next;
free(q);
--L->length;
}