单链表简介:
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。代码:
#include <stdio.h>
#include "stdlib.h"
typedef int ElemType;
typedef struct Node
{
ElemType data;
struct Node *next;
}Node,*LinkList;
//初始化
bool InitList(LinkList &L){
L=(Node *)malloc(sizeof(Node));//分配一个头节点
if(L==NULL){
return false;//内存不足 分配失败
}
L->next=NULL;//建立空的单链表
return true;
}
//判断是否为空
bool isEmpty(LinkList L){
if(L->next==NULL){
return true;
}else{
return false;
}
}
//头插法建表 逆向建立单链表
LinkList creatPreList1(LinkList &L){
//Node * 强调是一个结点 LinkList强调是链表
Node *s;
int x;
//创建头节点
L=(Node *)malloc(sizeof(Node));
//初始为空链表
L->next=NULL;
//输入结点的值
scanf("%d",&x);
while(x!=0){
s=(Node *)malloc(sizeof(Node));//创建新结点
s->data= x;
s->next=L->next; //指针域赋值
L->next=s;
scanf("%d",&x);
}
return L;
}
//头插
LinkList creatPreList2(LinkList &L,int n){
//Node * 强调是一个结点 LinkList强调是链表
Node *s;
int x;
//创建头节点
L=(Node *)malloc(sizeof(Node));
//初始为空链表
L->next=NULL;
for(int i=0;i<n;i++){
s=(Node *)malloc(sizeof(Node));//创建新结点
scanf("%d",&x);
s->data= x;
s->next=L->next; //指针域赋值
L->next=s;
}
return L;
}
//通过尾插法建立单链表
LinkList creatNextList1(LinkList &L){
//Node * 强调是一个结点 LinkList强调是链表
int x;
//创建头节点
L=(Node *)malloc(sizeof(Node));
Node *s,*r=L;//r指针始终指向当前链表的表尾
//输入结点的值
scanf("%d",&x);
while(x!=0){
s=(Node *)malloc(sizeof(Node));//创建新结点
s->data= x;
r->next=s;
r=s; //r指向新的表尾
scanf("%d",&x);
}
r->next=NULL;//尾结点指针置空
return L;
}
//指定结点的后插操作
bool creatNextList2(Node *p,int e){
if(p==NULL){
return false;
}
Node *s=(Node *)malloc(sizeof(Node));
if(s==NULL){
return false;//内存分配失败
}
s->data=e;//用结点s保存数据e
s->next=p->next;
p->next=s;
return true;
}
//指定结点的前插操作 插入某个元素 e
//偷天换日的操作
bool creatPreList3(Node *p,int e){
if(p==NULL){
return false;
}
Node *s=(Node *)malloc(sizeof(Node));
if(s==NULL){
return false;//内存分配失败
}
s->next=p->next;
p->next=s;//新结点s连接到p之后
s->data=p->data;//将p中元素赋值到s中去
p->data=e;
return true;
}
//删除第i个位置的元素,并用e返回删除的元素的值
//头结点可以看作是第0个结点 但不保存数据
bool deleList(LinkList &L,int i,int &e){
//1 先查找到 i-1 位置
if(i<1){
return false;
}
Node *p;//指针p指向当前扫描到的结点 p指向头节点
int j=0;//当前p指向的第几个结点
p=L;//L指向头节点,头节点是第0个结点 不存放数据
while(p!=NULL&&j<i-1){
//查找到第i-1个结点 p指向该结点
p=p->next;
j++;
}//如果插入的序号是length+1的话 p就指向null了 退出循环
if(p==NULL){
//当前p位置为空 表示已找完还没有数到第i个 说明i的插入位置不合理 i>n+1
return false;
}
if(p->next==NULL){
//第i-1个位置后 无其他结点
return false;
}
Node *q=p->next; //令q指向被删除元素
e=q->data;//用e返回元素的值
p->next=q->next;
free(q);
return true;
}
//在第i个位置插入e元素
bool insertList(LinkList &L,int i,int e){
//先找到第i-1个位置
if(i<1){
return false;
}
Node *p;//指针p指向当前扫描到的结点
int j=0;//当前p指向的第几个结点
p=L;//L指向头节点
while(p!=NULL&&j<i-1){
p=p->next;
j++;
}
if(p==NULL){
//i值不合法
return false;
}
//插入
Node *s=(Node *)malloc(sizeof(Node));
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
//通过位序查找结点
Node* getElem(LinkList L,int i){
//LinkList L,int i 也可以写为 Node *L,int i
if(i<0){
return NULL;
}
Node *p;//指针p指向当前扫描到的结点
int j=0;//当前p指向的第几个结点
p=L;//L指向头节点,头节点是第0个结点(不存放数据)
while(p!=NULL&&j<i){
//循环找到第i个结点
p=p->next;
j++;
}
return p;
}
//封装 插入
bool insert(LinkList &L,int i,int e){
//先找到第i-1个结点
Node *q=getElem(L,i-1);
//q结点后插元素e
return creatNextList2(q,e);
}
//直接在末尾添加元素
void addTail(LinkList &L,int x){
//先找到末尾
Node *n=L;
while(n->next!=NULL){
n=n->next;
}
//创建新结点s 保存元素x
Node *s=(Node *)malloc(sizeof(Node));
s->data=x;
//并指向新结点s
s->next=NULL;
n->next=s;
}
//按值查找
Node * locateElem(LinkList L,int e){
Node *p=L->next;
//从第一个结点开始查找
while(p!=NULL&&p->data!=e){
p=p->next;
}
return p;//找到后返回该结点指针,否则返回null
}
//单链表的长度
int length(LinkList L){
//带头节点的单链表的长度 画图理解哈
Node *p;
p=L->next;
int len=0;
while(p!=NULL){
p=p->next;
len++;
}
return len;
}
//遍历链表
void show(LinkList L){
Node *p;
p=L->next;
while(p!=NULL){
printf("%d",p->data);
p=p->next;
printf(" ");
}
}
//清空单链表
void free(LinkList &L) {
LinkList p;
while (L->next) {
p = L->next;
L->next = p->next;
delete(p);
}
}
//删除链表中最大的值(只有一个最大值)
void deleMaxNode(LinkList &L){
Node *pre=L,*p=L->next;
Node *maxPre,*maxP;
maxP=p,maxPre=pre;
while(p!=NULL){
if(maxP->data<p->data){//若找到个更大的一个结点
maxP=p; //更改maxP
maxPre=pre; //更改 maxPre
}
pre=p;
p=p->next;//pre p同步后移一个结点
}
//查找出最大值结点的前驱节点 *maxPre
maxPre->next=maxP->next;//删除maxP结点
delete(maxP);
}
//将单链表反转 利用头插法 画图理解
void Reverse(LinkList L){
Node *p,*q;
//将L拆分为两个部分
p=L->next;
L->next=NULL;
while(p!=NULL){
//向后移动位置
q=p;
p=p->next;
//头插
q->next=L->next;
L->next=q;
}
}
//在一个带有头节点的单链表L(至少有一个结点),使元素递增排列
//一定要画图理解 O(n*n)
void Sort(LinkList L){
Node *p,*pre,*q;
p=L->next->next; //p指针指向第二个结点 头节点是第0个结点
L->next->next=NULL;//构造只有一个数据结点的有序表 将L拆分为两个部分
while(p!=NULL){
q=p->next;//q指向p指针的后继结点
//在有序单链表中查找插入结点的前驱结点 *pre
pre=L;//从有序表开头开始比较,pre指向插入*p的前驱结点
while(pre->next!=NULL&&pre->next->data<p->data){
pre=pre->next;//在有序表中找插入*p的前驱结点*pre
}
//查找完之后 在*pre之后插入*p
p->next=pre->next;
pre->next=p;
p=q;//扫描单链表余下的结点
}
}
//有一个带头节点的单链表L,设计算法将其拆分为两个带头节点的单链表L1 L2
//要求L1的头节点是L的头节点
void split(LinkList &L,LinkList &L1,LinkList &L2){
Node *q,*r1,*p=L->next;//p指向第一个数据结点
L1=L;//L1利用原来链表的头节点
r1=L1;//r1始终指向L1的尾结点
L2=(Node *)malloc(sizeof(Node));//建立L2的头节点
L2->next=NULL;
while(p!=NULL){
//利用尾插法将p插入L1中
r1->next=p;
r1=p;
p=p->next;//p移向下一个结点
q=p->next;//q保存*p的后继结点
//利用头插法将*p插入L2中去
p->next=L2->next;
L2->next=p;
p=q;//p重新指向下一个结点
}
r1->next=NULL;//L1的尾结点置为空
}
//快慢指针问题 求单链表倒数第k个结点
Node* findK(LinkList L,int k){
Node *fast=L->next;
Node *slow=L->next;
//先让快指针先走k步
for(int i=1;i<k;i++){
fast=fast->next;
}
//然后同时让两个指针同时走
while(fast->next){
fast=fast->next;
slow=slow->next;
}
return slow;
}
//单链表找中间结点
Node* findMid(LinkList L){
Node *p=L->next;
Node *q=L->next;
while(q!=NULL&&q->next!=NULL){
p=p->next;
q=q->next->next;
}
return p;
}
int menu()
{
printf("\n\n\n**************************************菜单**********************************************\n");
printf("**********************************************************************************\n");
printf("**********1、 初始化, 2、判空\n");
printf("**********3、 前插建表 4、后插建表\n");
printf("**********5、 在第i个位置插入结点 6、删除第i个位置的元素\n");
printf("**********7、 删除单链表中最大值的结点(唯一):");
printf("**********8、 查找第i个位置 9、按值查找\n");
printf("**********10、 表长 11、遍历\n");
printf("**********12、 清空链表 13、将单链表逆置 \n");
printf("**********14、 让单链表升序排列 15、将单链表分割为L1 L2:\n");
printf("**********16、 找到单链表倒数第k个结点值 17、找到单链表中间结点值 \n");
printf("**********18、 在单链表末尾添加结点 19、退出 \n");
return 0;
}
int main()
{
LinkList L,L1,L2;
int i,e;
int option;
menu();
do
{
printf("\n输入选项:\n");
scanf("%d", &option);
switch (option)
{
case 1:
InitList(L);
printf("初始化成功\n");
break;
case 2:
if (isEmpty(L))
printf("线性表为空\n");
else
printf("线性表不为空\n");
break;
case 3:
printf("输入元素进行前插建表:\n");
creatPreList1(L);
//creatPreList2(L,4);
printf("前插建表成功,链表为:\n");
show(L);
break;
case 4:
printf("输入元素进行后插建表:\n");
creatNextList1(L);
printf("后插建表成功,链表为:\n");
show(L);
break;
case 5:
printf("在第i个位置插入e:\n");
scanf("%d%d",&i,&e);
insert(L,i,e);
break;
case 6:
printf("输入要删除的结点i\n");
scanf("%d", &i);
deleList(L,i,e);
printf("删除第%d个结点后,此链表为:",i);
show(L);
break;
case 7:
deleMaxNode(L);
printf("删除第最大值结点后,此链表为:");
show(L);
break;
case 8:
printf("请输入查找第i个元素:\n");
scanf("%d", &i);
Node *num = getElem(L,i);
if(num==NULL){
printf("输入位置不合法\n");
}else{
printf("查找的第 %d 个元素是:%d\n",i,num->data);
}
break;
case 9:
printf("按值查找:\n");
scanf("%d",&e);
locateElem(L,e)==NULL?printf("没有找到该元素:\n"):printf("该元素:%d\n",locateElem(L,e)->data);
break;
case 10:
printf("表长为%d:\n", length(L));
break;
case 11:
printf("遍历单链表为:\n");
show(L);
break;
case 12:
printf("清空单链表:\n");
free(L);
break;
case 13:
Reverse(L);
printf("将单链表逆置(利用头插法)之后的链表为:\n");
show(L);
break;
case 14:
Sort(L);
printf("升序之后的单链表:\n");
show(L);
break;
case 15:
split(L,L1,L2);
printf("链表拆分为L1和L2----L1为:");
show(L1);
printf("\n");
printf("链表拆分为L1和L2----L2为:");
show(L2);
break;
case 16:
printf("求单链表倒数第k个结点的值:");
int k;
scanf("%d", &k);
printf("单链表倒数第k个结点值为%d:\n",findK(L,k)->data);
break;
case 17:
printf("单链表中间结点值为%d:\n",findMid(L)->data);
break;
case 18:
printf("在单链表末尾添加结点:");
int x;
scanf("%d",&x);
addTail(L,x);
printf("添加结点后的单链表为:");
show(L);
break;
case 19: break;
default:
printf("输入选项不合法\n");
break;
}
} while (option != 19); //option==19退出
return 0;
}
代码运行图: