含表头的链表ADT(C语言版)

表头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");
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值