【数据结构】线性表|C/C++实现单链表的基本操作|2021王道数据结构笔记整理及测试

线性表的物理/存储结构之——单链表

【写在前面】本博客是笔者按照2021王道数据结构考研复习指导的视频课程整理的笔记,均已用编译器测试过可行,部分函数按照老师的提示做了一些代码健壮性的提升,可以放心使用。

目录导航(点击跳转)

  • 线性表的物理/存储结构之——单链表
    • 单链表的基本操作
      • 1、单链表的定义
      • 2、基本操作:初始化一个空的单链表
        • 2.1 带头结点的单链表
        • 2.2 不带头结点的单链表
      • 3、基本操作:单链表的判空
        • 3.1 带头结点的单链表
        • 3.2 不带头结点的单链表
      • 4、基本操作:单链表的按位查找
      • 5、基本操作:单链表的插入
        • 5.1 按位序插入(带头结点的单链表)
        • 5.2 按位序插入(不带头结点的单链表)
        • 5.3 指定节点的后插操作
        • 5.4 指定结点的前插操作
      • 6、 基本操作:单链表的打印操作
      • 7、单链表的删除操作
        • 7.1 按位序删除(带头结点的单链表)
        • 7.2 指定节点的删除
      • 8、单链表所有基本操作的测试代码

单链表的基本操作

1、单链表的定义

typedef struct LNode {
	int data;
	struct LNode *next;
}LNode,*LinkList;

等价于下面的写法:

//或者下面的写法
struct LNode{
    int data;
    struct LNode *next;
};
typedef struct LNode LNode;//取一个别名为LNode
typedef struct LNode *LinkList;//用LinkList表明这是一个指向LNode的指针

要表示一个单链表时,只需要声明一个头指针L,指向单链表的第一个结点。

LNode *L;
//或者
LinkList L;
//都表示声明一个指向单链表第一个结点的指针

2、基本操作:初始化一个空的单链表

2.1 带头结点的单链表
//初始化一个空的单链表(带头结点)
bool InitList(LinkList &L) {
	L = (LNode *)malloc(sizeof(LNode));//分配一个头结点
	if (L == NULL) {   //分配失败返回false
		return false;
	}
	L->next = NULL; //头结点之后暂时还没有结点
	return true;
}
2.2 不带头结点的单链表
//初始化一个空的单链表(不带头结点)
bool InitList(LinkList &L) {
	L = NULL;  //空表,暂时还没有任何结点,防止脏数据
	return true;
}

3、基本操作:单链表的判空

3.1 带头结点的单链表
//判断单链表是否为空(带头节点)
bool Empty(LinkList L) {
	if (L->next == NULL) {
		return true;
	}
	else {
		return false;
	}
}
3.2 不带头结点的单链表
//判断单链表是否为空(不带头节点)
bool Empty(LinkList L) {
	/*if (L == NULL) {
		return true;
	}
	else {
		return false;
	}*/
	//或者
	return(L==NULL); //L==NULL的运算结果本身就是一个bool型变量
}

4、基本操作:单链表的按位查找

//基础操作:单链表的按位查找
LNode *GetElem(LinkList L, int i) {
	int j = 1;
	LNode *p = L->next;
	if (i == 0)  //是第一个结点
		return L;
	if (i < 1)   //非法输入
		return NULL;
	while (p != NULL && j < i) {
		p = p->next;       
		j++;
	}
	return p;
}

【注】LNode *强调这是一个结点,LinkList强调这是一个单链表。

5、基本操作:单链表的插入

5.1 按位序插入(带头结点的单链表)
//基础操作:按位序插入:在第i个位置插入元素e
bool ListInsert(LinkList &L, int i, int e) {
	if (i < 1) {
		return false;
	}
	LNode *p;
	p = L;//p指向头结点,头结点是第0个结点(不存数据)
	//但单链表中实际存放的是头结点后面的结点,位序从1开始
	int j = 0;//表示当前p指向的是第几个结点
	while (p!=NULL && j < i-1) { //j
		p = p->next;
		j++;
	}
	if (p == NULL) {
		return false;
	}
	LNode *s = (LNode *)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
}
5.2 按位序插入(不带头结点的单链表)
//基础操作:按位序插入:在第i个位置插入元素e
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;
	p = L;//p指向头结点,头结点是第0个结点(不存数据)
	//但单链表中实际存放的是头结点后面的结点,位序从1开始
	/*********************1.找到第i-1个结点*******************************/
	int j = 1;//表示当前p指向的是第几个结点
	while (p != NULL && j < i - 1) {    //j
		p = p->next;
		j++;
	}
	if (p == NULL) {
		return false;
	}
	/*********************2.在第i-1个结点后面插入e**********************/
	LNode *s = (LNode *)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
}

5.3 指定节点的后插操作
	//基础操作:后插操作
bool InsertNextNode(LNode *p, int e) {
	if (p == NULL) {
		return false;
	}
	LNode *s = (LNode *)malloc(sizeof(LNode));
	if (s == NULL) {  //分配内存失败
		return false;
	}
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
}

封装后的按位序插入:(注意后插操作函数的定义要在按位序插入函数之前,不然会报错。)

//基础操作:后插操作
bool InsertNextNode(LNode *p, int e) {
	if (p == NULL) {
		return false;
	}
	/*********************2.在第i-1个结点后面插入e**********************/
	LNode *s = (LNode *)malloc(sizeof(LNode));
	if (s == NULL) {  //分配内存失败
		return false;
	}
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
}

//基础操作:按位序插入:在第i个位置插入元素e
bool ListInsert(LinkList &L, int i, int e) {
	if (i < 1) {
		return false;
	}
	LNode *p;
	p = L;//p指向头结点,头结点是第0个结点(不存数据)
	//但单链表中实际存放的是头结点后面的结点,位序从1开始
	/*********************1.找到第i-1个结点*******************************/
	int j = 0;//表示当前p指向的是第几个结点
	while (p!=NULL && j < i-1) {    //j
		p = p->next;
		j++;
	}
	return InsertNextNode(p, e);
}
5.4 指定结点的前插操作
//基础操作:前插操作
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->data = e;
	return true;

}

前插操作【王道书版本】

//基础操作:前插操作【王道书版本】
//在p结点之前插入结点s
bool InsertPriorNode(LNode *p, LNode *s) {
	if (p == NULL||s==NULL) {
		return false;
	}
	s->next = p->next;
	p->next = s;
	int temp = p->data;
	p->data = s->data;
	s->data = temp;
	return true;

}

6、 基本操作:单链表的打印操作

//基础操作:打印单链表数据
void DisplayList(LinkList L) {
	LNode *p;
	p = L->next;
	int i = 0;
	while (p!= NULL) {
		printf("%d  ", p->data);
		p = p->next;
		i++;
	}
	printf("一共有%d个元素\n",i);
}

7、单链表的删除操作

7.1 按位序删除(带头结点的单链表)
//基础操作:按位序删除
bool DeleteNode(LinkList &L, int i,int &e) {
	if (i < 1) {
		return false;
	}
	LNode *p;
	LNode *q;
	int j = 0;
	p = L;

	while (p != NULL && j < i - 1) {
		p = p->next;
		j++;
	}

	if (p == NULL) {
		return false;
	}
	if (p->next == NULL) {//第i-1个结点后面没有其他结点了
		return false;
	}

	q = p->next;
	e = q->data;
	p->next = q->next;
	
	free(q);
	return true;
}
7.2 指定节点的删除

【注】这里已按照王道视频课的提示进行了修改,当指定结点是超出现有结点的值时,不会报错,此时删除失败,函数返回值为false。

//基础操作:删除指定结点p
bool DeleteNode(LNode *p) {
	if (p == NULL) {
		return false;
	}
	LNode *q = p->next;
	if (q == NULL) {
		return false;
	}
	p->data = q->data;
	p->next = q->next;
	free(q);
	return true;
}

8、单链表所有基本操作的测试代码

代码可以在VS环境下编译运行,由于篇幅原因我将测试代码放在另一篇文章中,链接为【数据结构(二)】单链表|(全)测试代码|用C语言/C++实现单链表的定义、插入、删除、查找、打印输出等基本操作。
测试结果如下,包括单链表的定义,向其中插入元素及删除元素。
【数据结构】线性表|C/C++实现单链表的基本操作|2021王道数据结构笔记整理及测试_第1张图片

你可能感兴趣的:(数据结构,链表,单链表,c++,c语言)