C语言实现单链表的基本操作
一:单链表
1:定义
链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起的,每个结点只有一个链域的链表称为单链表(Single Linked List)。
2:特点
- 物理上不连续,逻辑上连续:可以将物理地址上不连续的内存空间连接起来,通过指针来对物理地址进行操作,实现逻辑上的连续(线性)。
- 头指针:单链表中每个结点的存储地址是存放在其前趋结点的next域中,而开始结点无前趋,故应设头指针head指向开始结点。链表由头指针唯一确定。
- 尾巴:终端结点无后继,故终端结点的指针域为空,即NULL。
二:单链表相关实现算法
L=(LinkList) malloc(sizeof (LinkList)); 在分配内存空间时,得注意malloc()函数的使用
1:单链表结构体定义
- typedef int Elemtype:指的是在当前程序中Elemtype就代表的int(整型)的数据类型,相当于给int类型起了一个别名。
- 其中LNode,*LinkList写在{}后面的目的是为了方便使用。
typedef int ElemType;
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
2:单链表的初始化
LinkList InitList(LinkList L){
L=(LinkList) malloc(sizeof (LinkList));
if(L==NULL){
return NULL;
}
L->next=NULL;
return L;
}
3:按照指定位序插入
按照位序插入 在第i个位置插入,插入的元素的值为e 思想:
- 首先判断i的值是否合法
- 初始化好一个指向头结点的指针
- 定义一个整型变量用来存储当前指针所指向结点的位置
- 其次将定义的指针移动到i-1结点点处
- 然后判断移动i-1位置的结点是否为NULL
- 在不为NULL的情况下,开辟一个新的LinkList类型的空间,其数据域中存入e
- 最后,进行连接结点的操作。
int ListInsert(LinkList L,int i,ElemType e){
if(i<1){
return 0;
}
LNode *p;
int j=0;
p=L;
while (p!=NULL&&j<i-1){
p=p->next;
j++;
}
p = NULL;
if(p){
return 0;
}
LNode *s= (LNode *)malloc(sizeof (LNode));
s->data=e;
s->next=p->next;
p->next=s;
return 1;
}
3:创建一个新结点
LNode* CreatNode(int data){
LNode *newNode=(LNode *) malloc(sizeof (LNode));
if(newNode==NULL){
printf("分配结点失败,请检查内存!");
return NULL;
}
newNode->data=data;
newNode->next=NULL;
return newNode;
}
4:指定结点的后插操作
后插操作:在p结点之后插入元素
思想:
- 首先对待插入的结点进行判断
- 然后利用malloc函数申请一个结点的内存空间
- 对该内存空间进行判断
- 将待插入的数据放到新建结点的数据域中
- 然后利用链表的连接操作即可
int InsertNextNode(LNode *p,ElemType e){
if(p==NULL){
return 0;
}
LNode *s=(LNode *) malloc(sizeof (LNode));
if(s==NULL){
printf("内存分配失败!");
return 0;
}
s->data=e;
s->next=p->next;
p->next=s;
return 1;
}
5:指定结点的前插操作
前插操作:在p结点之前插入元素
思想: 第一种:通过遍历单链表找到p的前驱结点q,然后在q结点后面插入一个新的元素。由于这种所耗费的时间复杂度为O(n)。
第二种:(偷天换日)–时间复杂度为O(1)
- 将新结点s连接的p之后
- 然后将p中的数据域中的值复制到新结点s
- 将p中的元素用待插入的元素进行覆盖
int InsertPriorNode(LNode *p,ElemType e){
if(p==NULL){
return 0;
}
LNode *s=(LNode *) malloc(sizeof (LNode));
if(s==NULL){
printf("内存分配失败!");
return 0;
}
s->next=p->next;
p->next=s;
s->data=p->data;
p->data=e;
return 1;
}
6:按位序删除
按位序删除 --i指的是第i个结点,e用来返回删除的结点的数据域的数值
思想:首先得遍历到第i-1个结点,对该节点进行判空以及对该节点的后继结点进行判空操作,然后修改指针的执行删除第i个结点。
int ListDelete(LinkList L,int i,ElemType e){
if(i<1){
return 0;
}
LNode *p;
int j=0;
p=L;
while (p!=NULL&&j<i-1){
p=p->next;
j++;
}
if(p==NULL){
return 0;
}
if(p->next==NULL) {
return 0;
}
LNode *q=p->next;
e=q->data;
p->next=q->next;
free(q);
return e;
}
7:指定结点的删除
由于删除该结点得修改前驱结点的next指针 方法1:传入头指针,循环寻找p的前驱结点 方法2:偷天换日(类似与结点前插的实现)–利用数据域中的值进行更新。
思想:
- 将待删除指针后面的数据域给待删除元素
- 然后只需将待删除指针后面的结点删除即可
int DeleteNodee(LNode *p){
if(p==NULL){
return 0;
}
LNode *q=p->next;
p->data=q->data;
p->next=q->next;
free(q);
return 1;
}
8:按位查找
LNode * GetElem(LinkList L,int i){
if(i<0){
return NULL;
}
LNode *p;
int j=0;
p=L;
while(p!=NULL&&j<i){
p=p->next;
j++;
}
return p;
}
9:按值查找
LNode * LocateElem(LinkList L,ElemType e){
LNode *p=L->next;
while (p!=NULL&&p->data!=e){
p=p->next;
}
return p;
}
10:求单链表的长度
int LengthList(LinkList L){
int len=0;
LNode *p=L->next;
while (p!=NULL){
len++;
p=p->next;
}
return len;
}
11:创键单链表
a:头插法:可用于链表的逆置
思想:
- 数据是通过输入来获取
- 没插入一个结点先申请一个结点所需的内存空间
- 然后利用头指针和新结点连接起来
LinkList List_HeadInsert(LinkList L){
LNode *s;
int x;
L=(LinkList *) malloc(sizeof(LNode));
L->next=NULL;
scanf("%d",&x);
while (x!=1000){
s=(LNode *) malloc(sizeof (LNode));
s->data=x;
s->next=L->next;
L->next=s;
scanf("%d",&x);
}
return L;
}
b:尾插法
思想:
- 用一个指针一直为表尾指针
- 利用表尾指针和新开辟的内存空间进行操作
LinkList List_TailInsert(LinkList L){
int x;
L=(LinkList) malloc(sizeof (LNode));
LNode *s,*r=L;
scanf("%d",&x);
while (x!=1000){
s=(LNode *) malloc(sizeof (LNode));
s->data=x;
r->next=s;
r=s;
scanf("%d",&x);
}
r->next=NULL;
return L;
}
三:完整代码
#include "stdio.h"
#include "stdlib.h"
typedef int ElemType;
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
LinkList InitList(){
LinkList L=(LinkList) malloc(sizeof (LinkList));
if(L==NULL){
return NULL;
}
L->next=NULL;
return L;
}
int ListInsert(LinkList L,int i,ElemType e){
if(i<1){
return 0;
}
LNode *p;
int j=0;
p=L;
while (p!=NULL&&j<i-1){
p=p->next;
j++;
}
if(p==NULL){
return 0;
}
LNode *s= (LNode *)malloc(sizeof (LNode));
s->data=e;
s->next=p->next;
p->next=s;
return 1;
}
LNode* CreatNode(int data){
LNode *newNode=(LNode *) malloc(sizeof (LNode));
if(newNode==NULL){
printf("分配结点失败,请检查内存!");
return NULL;
}
newNode->data=data;
newNode->next=NULL;
return newNode;
}
int InsertNextNode(LNode *p,ElemType e){
if(p==NULL){
return 0;
}
LNode *s=(LNode *) malloc(sizeof (LNode));
if(s==NULL){
printf("内存分配失败!");
return 0;
}
s->data=e;
s->next=p->next;
p->next=s;
return 1;
}
int InsertPriorNode(LNode *p,ElemType e){
if(p==NULL){
return 0;
}
LNode *s=(LNode *) malloc(sizeof (LNode));
if(s==NULL){
printf("内存分配失败!");
return 0;
}
s->next=p->next;
p->next=s;
s->data=p->data;
p->data=e;
return 1;
}
int ListDelete(LinkList L,int i,ElemType e){
if(i<1){
return 0;
}
LNode *p;
int j=0;
p=L;
while (p!=NULL&&j<i-1){
p=p->next;
j++;
}
if(p==NULL){
return 0;
}
if(p->next==NULL) {
return 0;
}
LNode *q=p->next;
e=q->data;
p->next=q->next;
free(q);
return e;
}
int DeleteNode(LNode *p){
if(p==NULL){
return 0;
}
LNode *q=p->next;
p->data=q->data;
p->next=q->next;
free(q);
return 1;
}
LNode * GetElem(LinkList L,int i){
if(i<0){
return NULL;
}
LNode *p;
int j=0;
p=L;
while(p!=NULL&&j<i){
p=p->next;
j++;
}
return p;
}
LNode * LocateElem(LinkList L,ElemType e){
LNode *p=L->next;
while (p!=NULL&&p->data!=e){
p=p->next;
}
return p;
}
int LengthList(LinkList L){
int len=0;
LNode *p=L->next;
while (p!=NULL){
len++;
p=p->next;
}
return len;
}
LinkList List_HeadInsert(LinkList L){
LNode *s;
int x;
L=(LinkList *) malloc(sizeof(LNode));
L->next=NULL;
scanf("%d",&x);
while (x!=1000){
s=(LNode *) malloc(sizeof (LNode));
s->data=x;
s->next=L->next;
L->next=s;
scanf("%d",&x);
}
return L;
}
LinkList List_TailInsert(LinkList L){
int x;
L=(LinkList) malloc(sizeof (LNode));
LNode *s,*r=L;
scanf("%d",&x);
while (x!=1000){
s=(LNode *) malloc(sizeof (LNode));
s->data=x;
r->next=s;
r=s;
scanf("%d",&x);
}
r->next=NULL;
return L;
}
int IsEmpty(LinkList L){
if(L->next==NULL){
return 1;
}
return 0;
}
void PrintList(LinkList L) {
LNode *p;
p=L->next;
printf("单链表的数据为");
while (p!=NULL){
printf("%d ",p->data);
p=p->next;
}
printf("\n");
}
int main(){
LinkList L = NULL;
L = InitList();
L=List_HeadInsert(L);
PrintList(L);
if(IsEmpty(L)==1){
printf("当前链表是空的!\n");
} else{
printf("当前链表不是空的!\n");
}
int len=LengthList(L);
printf("当前单链表的长度为%d\n",len);
LNode *s=LocateElem(L,2);
printf("查找成功!值为%d\n",s->data);
DeleteNode(s);
printf("删除成功!删除后的");
PrintList(L);
ListInsert(L,2,11);
printf("新增成功!新增后");
PrintList(L);
return 0;
}
|