数据结构—带头结点的单链表

前言

链式结构中,除了存数据元素信息外,还要存储它的后继元素的存储地址。我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。这两部分组成一个结点。n个结点链结成一个链表,即为线性表的链式存储结构。因为此链表的每个结点中只包含一个指针域,所以叫做单链表

  • 为了更加方便地对链表进行操作,会在单链表地第一个结点之前附设一个结点,称为头结点
  • 头结点地数据域可以不存储任何信息,指针域存储指向第一个结点地指针。

一、带头结点的单链表结构设计

typedef int ElemType;
//设计结点类型    
typedef struct ListNode {
	ElemType val;
	struct ListNode* next;//后继  结点类型的指针
}ListNode;

//链表  
typedef struct LinkList {
	ListNode* head;//head是头指针,指向头节点
	int cursize;//有效结点个数
};

二、头文件设计(SingleLink.h)


#ifndef SINGLELINK_H
#define SINGLELINK_H

//带头结点的单向链表设计
typedef int ElemType;
//设计结点类型    
typedef struct ListNode {
	ElemType val;
	struct ListNode* next;//后继  结点类型的指针
}ListNode;

//链表  
typedef struct LinkList {
	ListNode* head;//head是头指针,指向头节点
	int cursize;//有效结点个数
};

//接口声明……

#endif // !SINGLELINK_H

接口函数设计

//1、初始化
void InitList(LinkList* plist);
//2、清空链表元素---将有效结点依次删除
void Clear(LinkList* plist);
//3、销毁----清空链表+删除头结点
void Destory(LinkList* plist);
//4、判空操作
bool IsEmpty(LinkList* plist);
//5、头插法
bool InsertHead(LinkList* plist,ElemType val);
//6、尾插法
bool InsertTail(LinkList* plist, ElemType val);
//7、指定位置插入---pos 第几个有效结点
bool InsertPosindex(LinkList* plist,  int pos, ElemType val);
//8、头删法
bool DeleteHead(LinkList* plist);
//9、尾删法
bool DeleteTail(LinkList* plist);
//10、指定位置删除,返回删除结点的元素值
ElemType DeletePos(LinkList* plist, int pos);
//11、删除val元素对应的结点
bool DeleteVal(LinkList* plist, ElemType val);
//12、获取指定结点的前驱结点  
ListNode* GetPrevNode(LinkList* plist, ElemType val);
//13、获取指定结点的后驱结点    
ListNode* GetNextNode(LinkList* plist, ElemType val);
//14、删除指定结点的前驱结点
bool DeletePrevNode(LinkList* plist, ElemType val);
//15、删除指定结点的后驱结点 
bool DeleteNextNode(LinkList* plist, ElemType val);
//16、打印链表
void Show(LinkList* plist);

三、接口函数实现(SingleLink.cpp)

#include "SingleLink.h"
#include <malloc.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <cstdlib>

1、初始化

ListNode* Buynode(ElemType val,ListNode* next) {
	ListNode* p = (ListNode*)malloc(sizeof(ListNode));
	if (p == nullptr)return nullptr;
	//memset(p, 0, sizeof(ListNode));
	p->next = next;
	p->val = val;
	return p;
}
void InitList(LinkList* plist) {   
	assert(plist != nullptr);
	plist->head = Buynode(0,nullptr);//带头结点
	plist->cursize = 0;
}

2、清空链表元素—将有效结点依次删除

void Clear(LinkList* plist) {
	assert(plist != NULL);
	ListNode* p;
	while (plist->head->next != nullptr) {
		//1.指针指向待删除结点地址
		p = plist->head->next;
		//2.贯穿
		plist->head->next = p->next;
		//3.释放p结点
		free(p);
		plist->cursize--;
	}
	/*while (!IsEmpty(plist)) {
		DeleteHead(plist);   空间开销大
	}	*/
}

3、销毁链表----清空链表+删除头节点

void Destory(LinkList* plist) {
	assert(plist != nullptr);
	Clear(plist);
	free(plist->head);
	plist->head = nullptr;
}

4、判空操作

bool IsEmpty(LinkList* plist){
	assert(plist != nullptr);
	//return plist->cursize == 0; 两种方式
	return plist->head->next == nullptr;
}

5、头插法

bool InsertHead(LinkList* plist, ElemType val) {
	assert(plist != nullptr);
	ListNode* p = Buynode(val,nullptr);//生成新结点p  
	assert(p != nullptr);
	p->next = plist->head->next;
	plist->head->next = p;
	plist->cursize++;
	return true;
}

6、尾插法

bool InsertTail(LinkList* plist, ElemType val) {
	assert(plist != nullptr);
	ListNode* newnode = Buynode(val, nullptr);//生成新结点
	assert(newnode != nullptr);
	//找到尾结点 尾结点-> next=p
	ListNode* s = plist->head;
	while (s->next != nullptr) {
		s = s->next;
	}
	s->next = newnode;
    plist->cursize++;
	return true;
}

7、指定位置插入 pos----第几个有效结点

bool InsertPosindex(LinkList* plist, int pos, ElemType val) {
	assert(plist != nullptr && pos >= 0 && pos <= plist->cursize+1);
	ListNode* newnode = Buynode(val, nullptr);//新节点
	assert(newnode != nullptr);
	ListNode* p = plist->head;
	for (int i = 0; i < pos - 1; i++) {//找该位置前一个结点   
	     p = p->next;
	}
	newnode->next = p->next;//先牵右手
	p->next = newnode;//再牵左手
	plist->cursize++;
	return true;
}

8、头删法

bool DeleteHead(LinkList* plist) {
	assert(plist != NULL);
	if (IsEmpty(plist))return false;//空链表不需要删除
	//1.指针指向待删除结点地址
	ListNode* p = plist->head->next;
	//2.贯穿
	plist->head->next = p->next;
	//3.释放p结点
	free(p);	
	plist->cursize--;
	return true;
}

9、尾删法

bool DeleteTail(LinkList* plist) {
	assert(plist != nullptr);
	if (IsEmpty(plist))return false;//空链表不需要删除
	ListNode* p = plist->head;
	while (p->next->next != nullptr) {//找到尾结点的前一个结点p    
		p = p->next;
	}
	p->next = p->next->next;
	free(p->next);
	plist->cursize--;
	return true;
}

10、指定位置删除,返回删除结点的元素值

//pos 删除第几个有效结点
ElemType DeletePos(LinkList* plist, int pos) {
	assert(plist != nullptr && pos >= 1 && pos < plist->cursize);
	if (IsEmpty(plist))exit(EXIT_FAILURE);

	ListNode* pre = plist->head;
	int i = 0;
	while (i < pos-1) {
		pre = pre->next;//pre标记待删结点的前一个结点
		i++;
	}
	ListNode* p = pre->next;//p标记待删结点
	ElemType val = p->val;
	pre->next = p->next;
	//free(p);可以省略。p属于局部变量,不会出现野指针问题
	plist->cursize--;
	return val;
}

11、删除val元素对应的结点

bool DeleteVal(LinkList* plist, ElemType val) {
	assert(plist != nullptr);
	if (IsEmpty(plist))return false;
	ListNode* p = plist->head;
	//删除第一个出现的val结点
	while (p->next!=nullptr && p->next->val != val) {//待删结点的前一个结点
		p = p->next;
	}
	if (p->next == nullptr)return false;//没有找到val所在结点则直接返回
	ListNode* q = p->next;//待删结点
	p->next = p->next->next;
	free(q);	
	plist->cursize--;
	return true;
}

注:
1、这里删除的是val元素对应的第一个结点。有待修改为删除为删除val元素对应的所有结点。
2、如果找到指定位置,再调用DeletePos(LinkList* plist, int pos) 会增加开销,因为DeletePos存在重复for循环

12、获取指定结点的前驱结点

ListNode* GetPrevNode(LinkList* plist, ElemType val) {
	assert(plist != nullptr);
	if (IsEmpty(plist))return nullptr;
	ListNode* q = nullptr;
	for (ListNode* p = plist->head; p->next != nullptr; p = p->next) {
		if (p->next->val == val) {
			q = p;
			break;
		}
	}
	return q;
}

13、获取指定结点的后驱结点

ListNode* GetNextNode(LinkList* plist, ElemType val) {
	assert(plist != nullptr);
	ListNode* p = plist->head->next;
	for (; p != nullptr && p->val != val ; p = p->next) {
		;
	}
	return p != nullptr ? p->next : nullptr;
}

14、删除指定结点的前驱结点

bool DeletePrevNode(LinkList* plist, ElemType val) {
	assert(plist != nullptr);
	if (IsEmpty(plist))return false;//空链表不删除
	ListNode* q;
	for (ListNode* p = plist->head; p->next->next != nullptr; p = p->next) {
		if (p->next->next->val == val) {//p标记的是val所在结点的前前个结点
			q = p->next;
			p->next = p->next->next;
			free(q);
			break;
		}
	}
	plist->cursize--;
	return true;

}

15、删除指定结点的后驱结点

bool DeleteNextNode(LinkList* plist, ElemType val) {
	assert(plist != nullptr);
	if (IsEmpty(plist))return false;//空链表不删除

	ListNode* q;
	for (ListNode* p = plist->head->next; p != nullptr; p = p->next) {
		if (p->val == val) {//p标记的是val所在结点
			if (p->next == nullptr)return false;//尾结点不删除
			q = p->next;
			p->next = p->next->next;
			free(q);
			break;
		}
	}
	plist->cursize--;
	return true;
}

16、打印链表

void Show(LinkList* plist) {
	//方式一
	for (ListNode* p = plist->head ; p->next!=nullptr ; p = p->next) {
		printf("%5d", p->next->val);
	}
	printf("\n");

	//方式二
	/*for (ListNode* p = plist->head->next; p != nullptr; p = p->next) {
		printf("%5d", p->val);
	}*/
}
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值