线性表(2)链式存储

1.单链表

ADT

typedef struct LNode { //定义单链表节点类型
	int data; //每个节点存放一个数据元素
	struct LNode* next; //每个节点存放一个指向下一个节点的指针
}LNode,*LinkList;

按位查找

根据所给的i值,返回第i个元素

带头结点:

LNode* GetElem(LinkList L, int i) {
	if (i < 0)
		return NULL;
	LNode* p = L; //建立一个指向头节点的指针p
	int j = 0; //它指向节点的序号初始值为0
	while (p != NULL && j < i) { //循环找到第i个结点
		p = p->next;
		j++;
	}
	return p;
}

不带头结点:

LNode* GetElem(LinkList L, int i) {
	if (i <= 0)
		return NULL;
	LNode* p = L; //建立一个指向第一个结点的指针p
	int j = 1; //它指向节点的序号初始值为1
	while (p != NULL && j < i) { //循环找到第i个结点
		p = p->next;
		j++;
	}
	return p;
}

带头结点的函数从0号结点开始循环,当i等于0时直接返回头节点;不带头节点的函数从1号结点开始循环,所以要增加i=0的判断

按位查找的时间复杂度为O(n)

按值查找

按值查找,若某节点数据域的值等于给定值e,返回该点的指针

带头结点:

LNode* LocateElem(LinkList L, int e) {
	LNode* p = L->next;
	while (p != NULL && p->data != e) {
		p = p->next;
	}
	return p;
}

不带头结点:

LNode* LocateElem(LinkList L,int e) {
	LNode* p = L;
	while (p != NULL && p->data != e) {
		p = p->next;
	}
	return p;
}

按值查找的时间复杂度为O(n)

插入结点

将值为x的新结点插入到单链表的第i个位置上

1.向前插入

带头结点:

bool ListInsert(LinkList& L, int i, int e) {
	if (i < 1)
		return false;
	LNode* p = GetElem(L, i-1); //根据之前写的查找函数,查找第i-1个结点
	if (p == NULL) //插入位置i不合法
		return false;
	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s; 
	return true;
}

不带头结点:

bool ListInsert(LinkList& L, int i, int e) {
	if (i < 1)
		return false;
	if (i == 1) { //插入第一个结点的操作比较特殊
		LNode* s = (LNode*)malloc(sizeof(LNode));
		s->data = e;
		s->next = L;
		L = s; //头结点指向新结点
		return true;
	}
	LNode* p = GetElem(L, i - 1); //根据之前写的查找函数,查找第i-1个结点
	if (p == NULL) //插入位置i不合法
		return false;
	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
}

插入节点所花费的时间主要用于查找前一个结点,时间复杂度为O(n),但如果给定结点插入,时间复杂度为O(1)

2.向后插入

带头结点与不带头结点一样

bool InsertNextNode(LinkList& L, int i, int e) {
	if (i < 1)
		return false;
	LNode* p = GetElem(L, i);
	if (p == NULL)
		return false;
	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
}

3.更换结点内容的向前插入

在p结点之后创建新的结点s,并将e赋值,然后交换结点p与s的数据,相当于使结点s位于结点p之前,实现前插操作

bool InsertPriorNode(LNode* p, int e) {
	if (p == NULL)
		return false;
	LNode* s = (LNode*)malloc(sizeof(LNode));
	if (s == NULL) //分配内存失败(如内存不足)
		return false;
	s->next = p->next;
	p->next = s; 
	s->data = p->data; //将p中元素复制到s中
	p->data = e; //将e的值赋给p
	return true;
}

删除结点

带头结点:

bool DeleteNode(LinkList& L, int i, int& e) {
	if (i < 1)
		return false;
	LNode* p = GetElem(L, i - 1); //循环找到第i-1个结点
	if (p == NULL)
		return false;
	if (p->next == NULL) //第i-1个结点后已无结点
		return false;
	LNode* q = p->next; //令q指向将要删除的结点
	e = q->data; //用e返回元素的值
	p->next = q->next; //令p指向被删除结点的下一个结点
	free(q); //释放q
	return true;
}

不带头结点:

bool DeleteNode(LinkList& L, int i, int& e) {
	if (i < 1)
		return false;
	if (i == 1) {
		L = L->next;
		return true;
	}
	LNode* s = GetElem(L, i - 1); //循环找到第i-1个结点
	if (s == NULL) //i-1的值不合法
		return false;
	if (s->next == NULL) //i-1结点后无后继节点
		return false;
	LNode* q = s->next; //令q指向将要删除的结点
	e = q->data; 
	s->next = q->next; //令s指向被删除结点的下一个结点
	free(q); //释放q
	return false;
}

尾插法建立单链表

带头结点:

LinkList List_TailInsert(LinkList& L) { //正向建立单链表
	int x; //设置ElemType为整型
	L = (LNode*)malloc(sizeof(LNode)); //建立头节点
	LNode* s;
	LNode* r = L; //r为表尾指针
	scanf("%d", &x);
	while (x != 9999) {
		s = (LNode*)malloc(sizeof(LNode)); //在r结点后插入数据x
		s->data = x;
		r->next = s;
		r = s; //永远保持r指向最后一个节点
		scanf("%d", &x);
	}
	r->next = NULL;
	return L;
}

不带头结点:

LinkList LinkList_TailInsert(LinkList& L) {
	int x;
	scanf("%d", &x);
	L = (LNode*)malloc(sizeof(LNode));
	LNode* r = L;
	LNode* s;
	while (x != 9999) {
		s = (LNode*)malloc(sizeof(LNode));
		r->data = x;
		r->next = s;
		r = s;
		scanf("%d", &x);
	}
	r->next = NULL;
	return L;
}

注:VC++不支持使用scanf,可以用scanf_s替换,或者查找C4996解决方案

头插法建立单链表

带头结点:

LinkList List_HeadInsert(LinkList& L) { //逆向建立单链表
	int x;
	LNode* p;
	L = (LNode*)malloc(sizeof(LNode)); //创建头结点
	L->next = NULL; //初始为空链表
	scanf_s("%d", &x);
	while (x != 9999) {
		p = (LNode*)malloc(sizeof(LNode)); //创建新节点
		p->data = x;
		p->next = L->next;
		L->next = p; //将新结点插入表中,L为头指针
		scanf_s("%d", &x);
	}
	return L;
}

不带头结点:

LinkList List_HeadInsert(LinkList &L) {
	LNode* s;
	L = NULL;
	int x;
	scanf_s("%d", &x);
	while (x != 9999) {
		s = (LNode*)malloc(sizeof(LNode));
		s->data = x;
		s->next = L;
		L = s;
		scanf_s("%d", &x);
	}
	return L;
}

2.双链表

typedef struct DNode {
	int data;
	struct DNode* next,* prior;
}DNode,*DLinkList;

//初始化双链表
bool InitDLinkList(DLinkList& L) {
	L = (DNode*)malloc(sizeof(DNode)); //分配一个头结点
	if (L == NULL) //内存不足,分配失败
		return false;
	L->prior = NULL; //头结点的prior永远指向NULL
	L->next = NULL; //头结点之后暂时还没有结点
	return false;
}

//在p结点后插入s结点
bool InsertNextNode(DNode* p, DNode* s) {
	if (s == NULL || p == NULL)
		return false;
	s->next = p->next;
	if(p->next !=NULL) //如果p结点有后继结点
	    p->next->prior = s;
	p->next = s;
	s->prior = p;
}

//删除p结点的后继结点
bool DeleteNextNode(DNode* p) {
	if (p == NULL)
		return false;
	if (p->next == NULL);
	    return false;
	DNode* q = p->next;
	p->next = q->next;
	if (q->next != NULL) //q结点不是最后一个结点
		q->next->prior = p;
	free(q);
	return true;
}

//删除双链表,循环释放各个结点
void DestoryList(DLinkList &L) {
	while (L->next != NULL)
		DeleteNextNode(L);
	free(L); //释放头结点
	L = NULL; //头指针指向NULL
}

循环链表

在单链表和双链表中加入一些指针即可

静态链表

两种ADT,一种意思

#define MaxSize 50
struct Node {
	int data;
	int next;
};

typedef struct {
	int data;
	int next;
}SLinkList[MaxSize];

int main() {
	struct Node a[MaxSize];
	SLinkList b;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值