线性表-链表的简单操作

链表和顺序表的简单区别在于链表是一个一个的节点相连接起来的,通过指针的作用,形成逻辑上的像链子一样的效果。其实对链表的操作和对数组的操作是差不多的,数组是直接操作数据,链表通过节点,间接操作数据。我感觉就像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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值