链表和顺序表的简单区别在于链表是一个一个的节点相连接起来的,通过指针的作用,形成逻辑上的像链子一样的效果。其实对链表的操作和对数组的操作是差不多的,数组是直接操作数据,链表通过节点,间接操作数据。我感觉就像java的封装类一样,setItem(item)为节点赋值,getItem()指针得到节点的值。有点像哈。虽然我学java,但还是感觉用c写这些算法比较明白,也许当初老师就是用c教我们的吧,习惯了。
前面的数据结构没有多少的概念问题需要解释,大多数都在注释里面解释了。
typedef int datatype ;
typedef struct Node {
datatype data;//数据域
struct Node *pNext;//指向下一节点的指针域
}Node, *pNode;
还是有必要把结构体放在前面,明白了链表的结构,其他都只是延伸。
这里说的是简单的链表,基本操作都有,很容易明白。
最后说一下,记得要free节点空间。
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
typedef int datatype ;
typedef struct Node {
datatype data;//数据域
struct Node *pNext;//指向下一节点的指针域
}Node, *pNode;
void Init(pNode &pHead);//初始化
void CreateHead(pNode pHead);//头插法建表
void CreateTail(pNode pHead);//尾插法建表
bool isEmpty(pNode pHead);//判断是否为空
int length(pNode pHead);//求链表长度
void Traverse(pNode pHead);//遍历
void Insert(pNode pHead,int i, datatype item);//插入节点
bool Delete(pNode pHead, int i, datatype &item);//删除节点
void Merge(pNode a,pNode b,pNode &c);//合并俩有序增序链表
//======================================
//初始化
void Init(pNode &pHead) {
pHead = (pNode )malloc(sizeof(Node));
if(!pHead) {
printf("failed!\n");
exit(-1);
}
pHead->pNext = NULL;//空链表只有一个头结点而已
return;
}
//=======================================
//创建链表
/*
头插法建表,就是总是在头节点后面插入数据
*/
void CreateHead(pNode pHead) {
printf("输入节点个数:");
int n,i = 0;
scanf("%d",&n);
while( i < n) {
datatype item;
printf("请输入节点值:");
scanf("%d",&item);
pNode pNew = (pNode)malloc(sizeof(Node));//建立新节点
pNew->data = item;
pNew->pNext = pHead->pNext;//把头结点的后继做pNew的后继,
pHead->pNext = pNew;//然后把pNew做头结点的后继
i++;
}
return ;
}
//======================================
/*
尾插法建表:就是总在链表的最后插入数据
*/
void CreateTail(pNode pHead) {
printf("输入节点个数:");
int n,i = 0;
scanf("%d",&n);
pNode pTail = pHead ;//建一个尾节点
/*其实这个尾节点不是因为我取名叫pTail她就真成了尾巴,其实她开始时她还是头结点
插入值后她变成了pNext,在插入后变成pNext->pNext类推
*/
while( i < n) {
datatype item;
printf("请输入节点值:");
scanf("%d",&item);
pNode pNew = (pNode)malloc(sizeof(Node));//建立新节点
pNew->data = item;
pTail->pNext = pNew;//把pNew给尾节点做后继
pNew->pNext = NULL;//pNew后继为空
pTail = pNew;//让pNew做尾节点
i++;
}
return ;
}
//=====================================
//判断是否为空
bool isEmpty(pNode pHead) {
if(NULL == pHead->pNext ) {//头结点没有后继节点就代表空
printf("空!\n");
return true;
}else
return false;
}
//=====================================
//求链表长度
int length(pNode pHead) {
pNode p = pHead->pNext;
int count = 0;
if(isEmpty(pHead ))
return count;//空链表的长度为0
while(p) {
count++;
p = p->pNext ;//直到下一节点为空
}
return count;//返回长度
}
//=====================================
//遍历
void Traverse(pNode pHead) {
if(isEmpty(pHead)) {
return ;//空表就没啥遍历可言了
}
pNode p = pHead->pNext;
while(p) {
printf("%d ",p->data );//改数据类型记得的时候%d也要改
p = p->pNext ;
}
return;
}
//=====================================
//插入节点
/*
i代表要插入的位置,比如3代表item插入后她的位置就是3。
我们想该怎么插入:比如 a—b—c—d—e这样的链表,我要在第三个位置插入m,
变成a—b c—d—e这样子
\ /
m
那我的思维就是b不指向c了改为指向m,然后m指向c,当然前提还需要找到第三个位置
*/
void Insert(pNode pHead,int i, datatype item) {
//首先我们要判断插入位置是否合法,至少不能为负数吧
if(i < 1 || i > length(pHead)+1) {//比如我可以在e后面的第6个位置插入,但不能在第七个位置插入
printf("插入位置不合法!\n");
return;
}
int n = 0 ;
pNode p = pHead ;
while(n < i-1) {//假如插第三个位置,那我们要找到第二个数b(记住从0开始也就是 数组[1]号数那样)
p = p->pNext ;//这段就是遍历到要找的位置
n++;
}//最后的p停在位置b
//printf("《%d》",p->data);
pNode pNew = (pNode)malloc(sizeof(Node));//新建一个节点存放item
pNew->data = item;
pNew->pNext = p->pNext;//就是把b的后继c做m的后继
p->pNext = pNew;//然后把m做b的后继
//其实最后只要清楚b有后继,m有前驱后继,c有前驱,那么程序就没错
return;
}
//=============================================
//删除节点
/*
i代表要删除的位置,比如3代表删除第三个位置,并将值赋给item,&item指的是传引用,其实用*item然后传地址一样。
比如 a—b—c—d—e这样的链表,我删除第三个位置c,
变成 a—b— —d—e这样子
那我的思维是先找到b,然后就可以得到c和d,那么我们删除掉了c,也就是
b少了后继,d少了前驱,补上就行了
最后记得free(c)啊!
整体思想和插入差不多
一定记得free(c)哈!
*/
bool Delete(pNode pHead, int i, datatype &item) {
//首先我们要判断删除位置是否合法
if(i < 1 || i > length(pHead)) {
printf("删除位置不合法!\n");
return false;
}
int n = 0 ;
pNode p = pHead ;
while(n < i-1) {
p = p->pNext ;
n++;
}//最后的p停在位置b
//printf("《%d》",p->data);
pNode temp = p->pNext ;//也就是c啦
item = temp->data ;
// b少了后继,d少了前驱
p->pNext = temp->pNext;
free(temp);
temp = NULL ;
return true;
}
//=========================================
//合并俩有序增序链表
void Merge(pNode a,pNode b,pNode &c) {//c传引用已经说过了 不过不传也没错
pNode pa = a->pNext ;
pNode pb = b->pNext;
pNode pc = c;
while( pa && pb) {
if(pa->data < pb->data ) {
pc->pNext = pa;//把pa节点给pc当后继
pc = pc->pNext ;//pc = pa 道理一样
pa = pa->pNext ;//移到下一个节点
}else {
pc->pNext = pb;
pc = pc->pNext ;
pb = pb->pNext;
}
}
pc->pNext = pa ? pa : pb;//长的那个链表后面的全赋值过来就行了 不用一个一个的赋值了
free(a);//用过的a b其实已经没用了
free(b);
a = NULL;
b = NULL;
return;
}
int main() {
pNode p;
Init(p);
CreateTail(p);
Traverse(p);
printf("插入数据:\n");
//Insert(p, 0, 99);//其实测试数据很重要
//一个测插在第一个,一个插在最后位置
Insert(p,6,99);
//Insert(p,2,99);
Traverse (p);
printf("\n删除数据 \n");
int item;
//Delete(p,0,item);
if( Delete(p,6,item) )
printf("删除的是:%d\n",item);
printf("合并俩链表\n");
pNode a,b,c;
Init(a);Init(b);Init(c);
CreateTail(a);
CreateTail(b);
Merge(a,b,c);
Traverse(c);
return 0;
}