单链表的基本操作【不带头结点】
【*】表示与带头结点不一样
1.定义链表的数据结构
// 定义节点的数据结构
typedef struct LNode{
int data; // 数据域
struct LNode *next; // 指针域
} Node, *LinkList;
2.链表初始化【*】
// 链表初始化
LinkList InitLinkList(){
LinkList p;
p = 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; 【与带头结点的区别】 L = s; } return true; }
-
尾插法
// 插入元素【尾插法】 bool TailInsertElement(LinkList &L, int len){ /* 思路:头指针始终指向最后一个元素 */ LinkList head; // 定义指针用于暂时记录头指针的地址 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; // 节点赋值 if(i==0){ // 插入第一个元素与其他的不一样【与带头结点区别】 s->next = L; L = s; head = L; // 将当前第一个节点的地址作为首地址 }else{ s->next = NULL; // 新节点下一个next 指针域为空 L->next = s; // 将新节点插入到表尾 L = s; //移动头指针到新节点 } } L = head; //再将头指针重新指向链表首节点 return true; }
4.插入节点
- 指定节点之前插入节点【未给链表头指针】
// 指定节点之前插入节点【未给链表头指针】
bool InsertLocationBefore(Node *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;
}
- 在位置i插入节点【给了链表头指针】【*】
// 在位置i插入节点
bool InsertLocation(LinkList &L, int i, int e){
/*
思路: 找到要插入的位置的前一个位置
(1)链表节点从1开始
(2)注意传进来的链表可能是空的【无节点】
(3)当链表只有一个节点并且在位置1插入的时候与其他的节点插入不一样
链表: [node1] ->[node2]
*/
if(i < 1 )
return false;
int j = 1; // 记录当前节点的位置从1开始【与带头结点不同】
Node *s = L; // 指针指向当前节点
while (s != NULL && j < i - 1) // 循环目的是找到要指定的位置前一个节点
{
s = s->next; // 变更指针指向
j++;
}
Node *N = (Node *)malloc(sizeof(Node)); // 定义节点指针变量
if(N == NULL)
return false;
N->data = e;
if(s!=NULL && i == 1){ // 单链表只有一个节点时候在位置1插入与其他情况不一样
N->next = L;
L = N;
return true;
}else if(s==NULL && j > 1){ // 位置i非法,以及经历过循环,i位置超出链表的最大长度
return false;
}else if(s == NULL && j == 1){ // 链表是空表
if(i == 1){ // 在空表第一个位置插入
N->next = L;
L = N;
return true;
}else{ // 空表除第一个位置外插入非法
return false;
}
}
N->next = s->next;
s->next = N;
return true;
}
5.删除指定节点
// 删除指定节点
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;
}
6.按位查找元素【*】
// 按位查找元素
Node * GetElement(LinkList &L, int i){
/*
思路:通过移动指针变量,使用循环找到对应的位置
*/
if(L == NULL)
return NULL; // 空表
if(i < 1)
return NULL; // 位置非法
Node *p = L; // 记录指针指向的位置
int j = 1;
while ( p != NULL && j < i)
{
p = p -> next;
j++;
}
if(p == NULL)
return NULL; // 位置非法
return p;
}
7.按值查找【*】
// 按值查找第一个值为e的节点位置
int GetLocation(LinkList &L, int e){
/*
思路:指针不为空一直往后找,直到找到元素为止或者等指针为空的时候跳出循环
*/
if(L == NULL)
return NULL; // 空表
Node *p = L;
int j = 1;
while (p != NULL)
{
if(p->data != e){
p = p->next;
j++;
}else{
break;
}
}
if(p == NULL) // 指针为空的时候表示没有该元素返回-1
return -1;
return j;
}
8.求表长度【*】
// 求表长度
int Tablelength(LinkList L){
if(L == NULL)
return 0;
int length = 0;
Node * p = L;
while( p != NULL){
p = p->next;
length ++;
}
return length;
}
9.打印链表数据【*】
// 打印链表
void PrintElement(LinkList &L){
Node *p;
printf("链表元素打印输出: \n");
p = L; // 节点从当前节点开始
while (p != NULL)
{
printf("%d\n", p->data);
p = p->next;
}
}
10.main 函数调用
// main函数
int main(){
LinkList L = InitLinkList();
int length = Tablelength(L); // 打印表长度
printf("表长度为: %d\n", length);
// PreheadInsertElement(L, 4); // 头插入法
TailInsertElement(L,1); // 尾插法
PrintElement(L); // 打印
InsertLocation(L, 3, 666); // 指定位置元素之后插入
PrintElement(L); //打印
DelLocationNode(L, 2); // 删除指定节点
PrintElement(L); // 打印元素
InsertLocationBefore(L, 1000); //第三个节点之前插入1000【不传入头指针】
PrintElement(L);
int length2 = Tablelength(L); // 打印表长度
printf("表长度为: %d\n", length2);
Node * item = GetElement(L, 1); // 按位查找
if(item != NULL){
printf("按位查找元素:%d\n", item->data);
}else{
printf("查找失败!位置不合法或者超出索引\n");
}
int index = GetLocation(L,1000); // 按值查找
printf("按值查找:%d\n", index);
DelNode(L); // 指定节点删除【这里删除位置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 = 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;
if(i==0){
s->next = L; // 插入第一个元素和其他后面插入的方式不一样【与带头结点的区别】
L = s;
}
else{
s->next = L->next;
L->next = s;
}
}
return true;
}
// 插入元素【尾插法】
bool TailInsertElement(LinkList &L, int len){
/*
思路:头指针始终指向最后一个元素
*/
LinkList head; // 定义指针用于暂时记录头指针的地址
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; // 节点赋值
if(i==0){ // 插入第一个元素与其他的不一样【与带头结点区别】
s->next = L;
L = s;
head = L; // 将当前第一个节点的地址作为首地址
}else{
s->next = NULL; // 新节点下一个next 指针域为空
L->next = s; // 将新节点插入到表尾
L = s; //移动头指针到新节点
}
}
L = head; //再将头指针重新指向链表首节点
return true;
}
// 在位置i插入节点
bool InsertLocation(LinkList &L, int i, int e){
/*
思路: 找到要插入的位置的前一个位置
(1)链表节点从1开始
(2)注意传进来的链表可能是空的【无节点】
(3)当链表只有一个节点并且在位置1插入的时候与其他的节点插入不一样
链表: [node1] ->[node2]
*/
if(i < 1 )
return false;
int j = 1; // 记录当前节点的位置从1开始【与带头结点不同】
Node *s = L; // 指针指向当前节点
while (s != NULL && j < i - 1) // 循环目的是找到要指定的位置前一个节点
{
s = s->next; // 变更指针指向
j++;
}
Node *N = (Node *)malloc(sizeof(Node)); // 定义节点指针变量
if(N == NULL)
return false;
N->data = e;
if(s!=NULL && i == 1){ // 单链表只有一个节点时候在位置1插入与其他情况不一样
N->next = L;
L = N;
return true;
}else if(s==NULL && j > 1){ // 位置i非法,以及经历过循环,i位置超出链表的最大长度
return false;
}else if(s == NULL && j == 1){ // 链表是空表
if(i == 1){ // 在空表第一个位置插入
N->next = L;
L = N;
return true;
}else{ // 空表除第一个位置外插入非法
return false;
}
}
N->next = s->next;
s->next = N;
return true;
}
// 删除指定位置的节点
bool DelLocationNode(LinkList &L, int i){
/*
思路: 找到要删除节点的前一个节点
*/
if(L == NULL) // 空链表
return false;
if(i < 1)
return false;
int j = 1; // 记录当前节点的位置
Node *s = L; // 指针指向当前节点
while (s != NULL && j < i - 1) // 循环目的是找到要指定的位置的前一个节点
{
s = s->next; // 变更指针指向
j++;
}
if(!s) // 当前节点是空
return false;
if(s->next == NULL){ //当前节点后已无节点
if(i==1){ //这里当只有一个节点的时候使用原来带节点的逻辑是删不掉得特殊处理
free(s); // 将指向当前节点的内存空间释放
L = NULL; // 指针置空
return true;
};
return false;
}
Node *p = s->next; // p是要删除的节点
s->next = p->next;
free(p); // 指向要删除的节点的内存地址空间释放
return true;
}
// 指定节点之前插入节点【未给链表头指针】
bool InsertLocationBefore(Node *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 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;
}
// 打印链表
void PrintElement(LinkList &L){
Node *p;
printf("链表元素打印输出: \n");
p = L; // 节点从当前节点开始
while (p != NULL)
{
printf("%d\n", p->data);
p = p->next;
}
}
// 按位查找元素
Node * GetElement(LinkList &L, int i){
/*
思路:通过移动指针变量,使用循环找到对应的位置
*/
if(L == NULL)
return NULL; // 空表
if(i < 1)
return NULL; // 位置非法
Node *p = L; // 记录指针指向的位置
int j = 1;
while ( p != NULL && j < i)
{
p = p -> next;
j++;
}
if(p == NULL)
return NULL; // 位置非法
return p;
}
// 按值查找第一个值为e的节点位置
int GetLocation(LinkList &L, int e){
/*
思路:指针不为空一直往后找,直到找到元素为止或者等指针为空的时候跳出循环
*/
if(L == NULL)
return NULL; // 空表
Node *p = L;
int j = 1;
while (p != NULL)
{
if(p->data != e){
p = p->next;
j++;
}else{
break;
}
}
if(p == NULL) // 指针为空的时候表示没有该元素返回-1
return -1;
return j;
}
// 求表长度
int Tablelength(LinkList L){
if(L == NULL)
return 0;
int length = 0;
Node * p = L;
while( p != NULL){
p = p->next;
length ++;
}
return length;
}
// main函数
int main(){
LinkList L = InitLinkList();
int length = Tablelength(L); // 打印表长度
printf("表长度为: %d\n", length);
// PreheadInsertElement(L, 4); // 头插入法
TailInsertElement(L,1); // 尾插法
PrintElement(L); // 打印
InsertLocation(L, 3, 666); // 指定位置元素之后插入
PrintElement(L); //打印
DelLocationNode(L, 2); // 删除指定节点
PrintElement(L); // 打印元素
InsertLocationBefore(L, 1000); //第三个节点之前插入1000【不传入头指针】
PrintElement(L);
int length2 = Tablelength(L); // 打印表长度
printf("表长度为: %d\n", length2);
Node * item = GetElement(L, 1); // 按位查找
if(item != NULL){
printf("按位查找元素:%d\n", item->data);
}else{
printf("查找失败!位置不合法或者超出索引\n");
}
int index = GetLocation(L,1000); // 按值查找
printf("按值查找:%d\n", index);
DelNode(L); // 指定节点删除【这里删除位置1的节点】
PrintElement(L);
system("pause");
return 0;
}