表头L为指针,指向一个结构体,L->next表示L指向的结构体中的结构的next域,这个next存储的是链表第一个节点的地址,所以L->next表示链表第一个节点的地址,所以L->next->element表示链表第一个节点element域中的值。
当表头L(指针)作为自定义函数参数传入时,L是作为一个临时变量存在的,所以对L进行的操作,并不改变表头的地址本身。但是当L->next作为左值出现时,却会改变L->next的值。所以,当表头L(指针)作为自定义函数参数传入时,函数只创建指针L的临时变量,而不创建结构体临时变量。编写代码时,当L->next作为左值出现时,应注意L->next的值被改变是否是有意为之。
测试代码代码如下:
<span style="font-size:12px;">#include <stdio.h>
#include <malloc.h>
struct Test;
typedef Test * Node;
struct Test
{
int num;
Node next;
};
int main()
{
Node A, B;
A = (Node)malloc(sizeof(struct Test));
B = (Node)malloc(sizeof(struct Test));
void F(Node L, Node M);
A -> next = NULL;
A -> num = 1;
B -> next = A;
B -> num = 2;
printf("A's add is%p\nA->next is%p\n", A, A -> next);
F(A, B);
}
void F(Node L, Node M)
{
L -> next = M -> next;
L = M;
printf("L's add is%p\nL->next is%p\n", L, L -> next);
}
//输出值表明A L L->next 表示同一地址 均为A地址,A->next为NULL </span>
链表ADT代码头文件:
#ifndef MYLIST_H_
#define MYLIST_H_
#pragma warning(disable:4996)
#include <stdbool.h>
#define OVERFLOW -1 //溢出时调用exit返回-1
#define LIST_IS_EMPTY -2
struct MyListNode;
typedef struct MyListNode * Node; //指向节点的指针
typedef struct MyListNode * List; //指向表头的指针
typedef int ElemType;
/*以下为接口函数声明*/
bool IsEmpty(List L); //输入表头,表为空输出true
List InitList(void); //输入表头,为其开辟内存,初始化一个空表
Node NewNode(ElemType E);//输入E,新建数据域为E的节点,返回节点地址
List DestroyList(List L);//销毁表,释放空间
void ClearList(List L);//清空表,仅留表头,其他空间释放
void AddFront(List L, Node N, ElemType E);//为表L的节点N增加前驱,数据域为E
void AddRear(List L, Node N, ElemType E);//增加后继
void AddTail(List L, ElemType E);//在表尾增加数据域为E的节点
void DeleteElem(List L, ElemType E);//删除表L中数据域与E值相同的节点
int ListLength(List L);//返回表中节点数,表头不计在内
void LinkList(List L1, List L2);//将表L2接在L1后面
void PrintList(List L);//顺序输出表中数据
#endif
实现代码:
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include "MyList.h"
struct MyListNode
{
ElemType element;
Node next; //指向下一个节点的指针
};
bool IsEmpty(List L)
{
return L->next == NULL;
}
Node NewNode(ElemType E)
{
Node N;
N = (Node)malloc(sizeof(struct MyListNode));
if (N == NULL)
exit(OVERFLOW);
N->next = NULL;
N->element = E;
return N;
}
List InitList(void)
{
List L;
L = (List)malloc(sizeof(struct MyListNode));
if (L == NULL)
exit(OVERFLOW);
L->next = NULL;
return L;
}
/*void InitList(List L) //相比于上面那一种写法,哪一种更好
{
L = (List)malloc(sizeof(struct MyListNode));
if(L == NULL)
exit(OVERFLOW);
L->next = NULL;
}*/
List DestroyList(List L)
{
Node tempN;
while (L != NULL){
tempN = L->next;
free(L);
L = tempN;
}
return NULL; //防止表头成为野指针
}
void ClearList(List L)//表头指向的内存不被释放
{
Node tempN;
L = L->next;
while (L != NULL){
tempN = L->next;
free(L);
L = tempN;
}
}
void AddFront(List L, Node N, ElemType E)
{
Node frontN = NewNode(E);
while (L->next != N)
L = L->next;
frontN->next = L->next;
L->next = frontN;
}
void AddRear(List L, Node N, ElemType E)
{
Node RearN = NewNode(E);
RearN->next = N->next;
N->next = RearN;
}
void AddTail(List L, ElemType E)
{
while (L->next != NULL)
L = L->next;
L->next = NewNode(E);
}
//DeleteElem算法,遍历链表,遇到符合条件的节点,让其前驱指向其后继,free当前节点,L指向后继,继续遍历
void DeleteElem(List L, ElemType E)
{//L为遍历指针,frontN为L的前驱指针,tempN为临时存储L后继的指针,配合free使用
Node tempN, frontN = L;
L = L->next;//排除头结点干扰
while (L != NULL){
if (L->element == E){
tempN = L->next;
free(L);
L = tempN;
frontN->next = tempN;
}
else{
frontN = L;
L = L->next;
}
}
}
int ListLength(List L)
{
int sum = 0;
L = L->next; //从第一个节点开始数,除去表头干扰
while (L != NULL){
sum++;
L = L->next;
}
return sum;
}
void LinkList(List L1, List L2)
{
while (L1->next != NULL)
L1 = L1->next;
L1->next = L2->next;
free(L2);//删除表L2表头
L2 = NULL;//防止L2表头变为野指针
}
void PrintList(List L)
{
if (IsEmpty(L))
exit(LIST_IS_EMPTY);//L为空则报错
L = L->next;//头结点element域为空,所以从第一个节点开始输出
while (L != NULL){
printf("%d ", L->element);
L = L->next;
}
printf("\n");
}
测试代码:
#include <stdio.h>
#include <stdlib.h>
#include "MyList.h"
int main()
{
List L1, L2;
int length1, length2, e, i;
//length1表示L1节点总数,e用于临时存储element键入值
L1 = InitList();//初始化链表
L2 = InitList();
printf("输入第一个表中的总节点数:");
scanf("%d", &length1);
printf("输入%d个节点的element值:", length1);
for (i = 0; i < length1; i++){
scanf("%d", &e);
AddTail(L1, e);
}
printf("输入第二个表中的总节点数:");
scanf("%d", &length2);
printf("输入%d个节点的element值:", length2);
for (i = 0; i < length2; i++){
scanf("%d", &e);
AddTail(L2, e);
}
printf("表L1为:");
PrintList(L1);
printf("表L2为:");
PrintList(L2);
DeleteElem(L1, 3);
printf("执行删除3操作后的表L1为:");
PrintList(L1);
LinkList(L1, L2);
length1 = ListLength(L1);
printf("合并后的表L1长为:%d\n", length1);
printf("合并后的表L1为:");
PrintList(L1);
system("pause");
}