单链表的基本操作【带头结点】
1.定义链表的数据结构
// 定义节点的数据结构
typedef struct LNode{
int data; // 数据域
struct LNode *next; // 指针域
} Node, *LinkList;
2.链表初始化
// 链表初始化并返回链表首地址
LinkList InitLinkList(){
LinkList p;
p = (LinkList)malloc(sizeof(Node));
p->data = NULL; // 头结点【指针指向头结点】
p->next = NULL;
return p;
}
3.插入元素
-
头插法
// 插入元素【头插法】 bool PreheadInsertElement(LinkList &L, int len){ /* 思路:始终在头指针后面插入节点 */ Node *s; int e; if(len < 1) return false; printf("输入数据:\n"); for(int i=0; i < len; i++){ s = (Node *)malloc(sizeof(Node)); scanf("%d", &e); s->data = e; s->next = L->next; // 头指针的下一个节点先赋值给新节点【这两步顺序不能乱】 L->next = s; // 再将头指针的下一个节点指向新节点 } return true; }
-
尾插法
// 插入元素【尾插法】 bool TailInsertElement(LinkList &L, int len){ /* 思路:头指针始终指向最后一个元素 */ LinkList head = L; // 记录链表的首地址 Node *s; int e; if(len < 1) return false; printf("请输入数据: \n"); for(int i = 0; i<len; i++){ s = (Node *)malloc(sizeof(Node)); scanf("%d", &e); s->data = e; // 构造新节点 s->next = NULL; // 新节点的next指针域为空 L->next = s; // 连接新节点 L = s; // 移动指针到新节点上 } L = head; // 重新将头指针指向最开始的首地址 return true; }
4.插入节点
-
指定位置之后插入节点
// 指定位置之后插入节点 bool InsertLocationBack(LinkList &L, int i, int e){ /* 思路: 找到要位置i 链表: [NULL] ->[node1] ->[node2] ->[node3] | */ if(i < 1 ) // 链表第一个位置(下标0)是头结点 return false; int j = 0; // 记录当前节点的位置【一开始是头结点】 Node *s = L; // 指针指向当前节点 while (s != NULL && j < i) // 循环目的是找到要指定的位置 { s = s->next; // 变更指针指向 j++; } if(s==NULL) // 位置i 非法 return false; Node *N = (Node *)malloc(sizeof(Node)); // 定义节点 if(N == NULL) // 内存分配失败 return false; N->data = e; N->next = s->next; s->next = N; return true; }
-
指定节点之前插入节点【未给链表头指针】
// 指定节点之前插入节点【未给链表头指针】 bool InsertLocationBefore(LinkList &p, int e){ /* 思路: 创建新节点与指定节点数据交换 时间复杂度O(1) */ if(!p) return false; Node *s; s = (Node *)malloc(sizeof(Node)); if (!s) return false; //内存分配失败 s->next = p->next; // 将指定的节点赋值给新节点的指针next指针 p->next = s; // 将p的指针 s->data = p->data; // 交换数据 p->data = e; return true; }
-
指定位置之前插入数据【给了链表头指针】
// 指定位置之前插入数据【给了链表头指针】 bool InsertLocationBeforeWithHead(LinkList &L, int i, int e){ /* 思路: 找到i个的前一个位置 时间复杂度O(n) */ if(i < 1) return false; Node *s = L; Node *N; // 定义新节点指针 int j = 0; // 记录当前节点的位置 while (s !=NULL && j < i - 1) // 找到第i个位置的前一个节点 { s = s->next; j++; } if(s == NULL) // 插入位置i非法 return false; N = (Node *)malloc(sizeof(Node)); if(!N) return false; // 申请内存地址失败 N->data = e; N->next = s->next; // 指针交换 s->next = N; return true; }
5.删除节点
// 删除指定位置的节点
bool DelLocationNode(LinkList &L, int i){
/*
思路: 找到要删除节点的前一个节点
*/
if(i < 1)
return false;
int j = 0; // 记录当前节点的位置
Node *s = L; // 指针指向当前节点
while (s != NULL && j < i - 1) // 循环目的是找到要指定的位置的前一个节点
{
s = s->next; // 变更指针指向
j++;
}
if(!s) // 当前节点是空
return false;
if(s->next == NULL) //当前节点后已无节点
return false;
Node *p = s->next; // p是要删除的节点
s->next = p->next;
free(p); // 指向要删除的节点的内存地址空间释放
return true;
}
6.删除指定节点
// 删除指定节点
bool DelNode(LinkList &p){
/*
思路:与删除节点的下一个节点交换数据,并将下一个节点删除【王道书上的有bug】
*/
if(p == NULL){
return false; // 节点为空
};
Node * s = p->next; // 要删除的节点的后一个节点
if(s == NULL){ // 要删除的节点为链表最后一个节点
free(p); // 直接将p指向的节点内存空间释放
p = NULL;
return true;
};
int temp = s->data; // 交换两个节点的数据
s->data = p->data;
p->data = temp;
// 此时节点s 为要删除的节点了
p->next = s->next;
free(s);
return true;
}
7.按位查找元素
// 按位查找元素
Node * GetElement(LinkList &L, int i){
/*
思路: 直接找到i个位置的节点将指针返回
*/
if(i < 1)
return NULL;
Node * p = L;
int j = 0; // 当前节点从头结点开始
while (p != NULL && j < i)
{
p = p->next;
j++;
}
if(p == NULL)
return NULL; // 位置非法【这里讲课视频也有bug, 存在索引超出范围也是查找不到的直接返空】
return p;
}
8.按值查找
// 按值查找第一个值为e的节点位置
int GetLocation(LinkList &L, int e){
/*
思路:指针不为空一直往后找,直到找到元素为止或者等指针为空的时候跳出循环
*/
Node *p = L;
int j = 0;
while (p != NULL)
{
if(p->data != e){
p = p->next;
j++;
}else{
break;
}
}
if(p == NULL) // 指针为空的时候表示没有该元素返回-1
return -1;
return j;
}
9.求表长度
// 求表长度
int Tablelength(LinkList L){
int length = 0;
Node * p = L;
while( p != NULL){
p = p->next;
length ++;
}
return length;
}
10.打印链表数据
// 打印链表数据
void PrintElement(LinkList &L){
Node *p;
p = L->next; // 从第1个位置才开始有数据
printf("数据打印: \n");
while (p != NULL)
{
printf("%d\n", p->data);
p = p->next;
}
}
7.main 函数调用
// main函数
int main(){
LinkList L = InitLinkList();
// PreheadInsertElement(L, 4); // 头插法
TailInsertElement(L,2); // 尾插法
PrintElement(L); // 打印链表
InsertLocationBack(L, 2, 666); // 按位插入
PrintElement(L); //打印
DelLocationNode(L, 2); // 删除指定位置的元素
PrintElement(L); // 打印元素
InsertLocationBefore(L->next->next->next, 1000); //第三个节点之前插入1000【不传入头指针】
PrintElement(L);
InsertLocationBeforeWithHead(L, 3, 8888); // 在第三个位置之前插入8888【传入头指针】
PrintElement(L);
Node * item = GetElement(L, 9); // 按位查找元素
if(item != NULL){
printf("按位查找的元素:%d\n", item->data);
}else{
printf("查找失败!位置不合法或者超出索引\n");
}
int value = GetLocation(L, 8888); // 按值查找
printf("按值查找位置:%d\n", value);
PrintElement(L);
int length = Tablelength(L); // 打印表长度
printf("表长度为: %d\n", length);
DelNode(L->next); // 指定节点删除【这里删除位置1的节点】
PrintElement(L);
DelNode(L->next); // 指定节点删除【这里删除位置1的节点】
PrintElement(L);
DelNode(L->next); // 指定节点删除【这里删除位置1的节点】
PrintElement(L);
system("pause");
return 0;
}
完整代码
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
#define InitSize 5
/*
单链表-基本操作【带头结点】
*/
// 定义节点的数据结构
typedef struct LNode{
int data; // 数据域
struct LNode *next; // 指针域
} Node, *LinkList;
// 链表初始化
LinkList InitLinkList(){
LinkList p;
p = (LinkList)malloc(sizeof(Node));
p->data = NULL; // 头结点【指针指向头结点】
p->next = NULL;
return p;
}
// 插入元素【头插法】
bool PreheadInsertElement(LinkList &L, int len){
/*
思路:始终在头指针后面插入节点
*/
Node *s;
int e;
if(len < 1)
return false;
printf("输入数据:\n");
for(int i=0; i < len; i++){
s = (Node *)malloc(sizeof(Node));
scanf("%d", &e);
s->data = e;
s->next = L->next; // 头指针的下一个节点先赋值给新节点【这两步顺序不能乱】
L->next = s; // 再将头指针的下一个节点指向新节点
}
return true;
}
// 插入元素【尾插法】
bool TailInsertElement(LinkList &L, int len){
/*
思路:头指针始终指向最后一个元素
*/
LinkList head = L; // 记录链表的首地址
Node *s;
int e;
if(len < 1)
return false;
printf("请输入数据: \n");
for(int i = 0; i<len; i++){
s = (Node *)malloc(sizeof(Node));
scanf("%d", &e);
s->data = e; // 构造新节点
s->next = NULL; // 新节点的next指针域为空
L->next = s; // 连接新节点
L = s; // 移动指针到新节点上
}
L = head; // 重新将头指针指向最开始的首地址
return true;
}
// 指定位置之后插入节点
bool InsertLocationBack(LinkList &L, int i, int e){
/*
思路: 找到要位置i
链表: [NULL] ->[node1] ->[node2] ->[node3] |
*/
if(i < 1 ) // 链表第一个位置(下标0)是头结点
return false;
int j = 0; // 记录当前节点的位置【一开始是头结点】
Node *s = L; // 指针指向当前节点
while (s != NULL && j < i) // 循环目的是找到要指定的位置
{
s = s->next; // 变更指针指向
j++;
}
if(s==NULL) // 位置i 非法
return false;
Node *N = (Node *)malloc(sizeof(Node)); // 定义节点
if(N == NULL) // 内存分配失败
return false;
N->data = e;
N->next = s->next;
s->next = N;
return true;
}
// 指定节点之前插入节点【未给链表头指针】
bool InsertLocationBefore(LinkList &p, int e){
/*
思路: 创建新节点与指定节点数据交换
时间复杂度O(1)
*/
if(!p)
return false;
Node *s;
s = (Node *)malloc(sizeof(Node));
if (!s)
return false; //内存分配失败
s->next = p->next; // 将指定的节点赋值给新节点的指针next指针
p->next = s; // 将p的指针
s->data = p->data; // 交换数据
p->data = e;
return true;
}
// 指定位置之前插入数据【给了链表头指针】
bool InsertLocationBeforeWithHead(LinkList &L, int i, int e){
/*
思路: 找到i个的前一个位置
时间复杂度O(n)
*/
if(i < 1)
return false;
Node *s = L;
Node *N; // 定义新节点指针
int j = 0; // 记录当前节点的位置
while (s !=NULL && j < i - 1) // 找到第i个位置的前一个节点
{
s = s->next;
j++;
}
if(s == NULL) // 插入位置i非法
return false;
N = (Node *)malloc(sizeof(Node));
if(!N)
return false; // 申请内存地址失败
N->data = e;
N->next = s->next; // 指针交换
s->next = N;
return true;
}
// 删除指定位置的节点
bool DelLocationNode(LinkList &L, int i){
/*
思路: 找到要删除节点的前一个节点
*/
if(i < 1)
return false;
int j = 0; // 记录当前节点的位置
Node *s = L; // 指针指向当前节点
while (s != NULL && j < i - 1) // 循环目的是找到要指定的位置的前一个节点
{
s = s->next; // 变更指针指向
j++;
}
if(!s) // 当前节点是空
return false;
if(s->next == NULL) //当前节点后已无节点
return false;
Node *p = s->next; // p是要删除的节点
s->next = p->next;
free(p); // 指向要删除的节点的内存地址空间释放
return true;
}
// 删除指定节点
bool DelNode(LinkList &p){
/*
思路:与删除节点的下一个节点交换数据,并将下一个节点删除【王道书上的有bug】
*/
if(p == NULL){
return false; // 节点为空
};
Node * s = p->next; // 要删除的节点的后一个节点
if(s == NULL){ // 要删除的节点为链表最后一个节点
free(p); // 直接将p指向的节点内存空间释放
p = NULL;
return true;
};
int temp = s->data; // 交换两个节点的数据
s->data = p->data;
p->data = temp;
// 此时节点s 为要删除的节点了
p->next = s->next;
free(s);
return true;
}
// 按位查找元素
Node * GetElement(LinkList &L, int i){
/*
思路: 直接找到i个位置的节点将指针返回
*/
if(i < 1)
return NULL;
Node * p = L;
int j = 0; // 当前节点从头结点开始
while (p != NULL && j < i)
{
p = p->next;
j++;
}
if(p == NULL)
return NULL; // 位置非法【这里讲课视频也有bug, 存在索引超出范围也是查找不到的】
return p;
}
// 按值查找第一个值为e的节点位置
int GetLocation(LinkList &L, int e){
/*
思路:指针不为空一直往后找,直到找到元素为止或者等指针为空的时候跳出循环
*/
Node *p = L;
int j = 0;
while (p != NULL)
{
if(p->data != e){
p = p->next;
j++;
}else{
break;
}
}
if(p == NULL) // 指针为空的时候表示没有该元素返回-1
return -1;
return j;
}
// 打印链表数据
void PrintElement(LinkList &L){
Node *p;
p = L->next; // 从第1个位置才开始有数据
printf("数据打印: \n");
while (p != NULL)
{
printf("%d\n", p->data);
p = p->next;
}
}
// 求表长度
int Tablelength(LinkList L){
int length = 0;
Node * p = L;
while( p != NULL){
p = p->next;
length ++;
}
return length;
}
// main函数
int main(){
LinkList L = InitLinkList();
// PreheadInsertElement(L, 4); // 头插法
TailInsertElement(L,2); // 尾插法
PrintElement(L); // 打印链表
InsertLocationBack(L, 2, 666); // 按位插入
PrintElement(L); //打印
DelLocationNode(L, 2); // 删除指定位置的元素
PrintElement(L); // 打印元素
InsertLocationBefore(L->next->next->next, 1000); //第三个节点之前插入1000【不传入头指针】
PrintElement(L);
InsertLocationBeforeWithHead(L, 3, 8888); // 在第三个位置之前插入8888【传入头指针】
PrintElement(L);
Node * item = GetElement(L, 9); // 按位查找元素
if(item != NULL){
printf("按位查找的元素:%d\n", item->data);
}else{
printf("查找失败!位置不合法或者超出索引\n");
}
int value = GetLocation(L, 8888); // 按值查找
printf("按值查找位置:%d\n", value);
PrintElement(L);
int length = Tablelength(L); // 打印表长度
printf("表长度为: %d\n", length);
DelNode(L->next); // 指定节点删除【这里删除位置1的节点】
PrintElement(L);
DelNode(L->next); // 指定节点删除【这里删除位置1的节点】
PrintElement(L);
DelNode(L->next); // 指定节点删除【这里删除位置1的节点】
PrintElement(L);
system("pause");
return 0;
}