数据结构学习笔记(二)------------线性表之链式存储

代码可能比较杂乱,严蔚敏书上要求实现的基本都实现了,只不过一些功能需要单独使用。

-------链表的创建、插入、删除、归并排序

希望各位老师给我指出不足

代码如下:

 

// 线性链表.cpp : 定义控制台应用程序的入口点。
//

# include "stdafx.h"
# include <stdio.h>
# include <stdlib.h>
/*---------------------------------------------------*/
# define MAX_SIZE 20
# define OK 1
# define ERROR 0
/*===================================================*/
typedef struct List
{
	int data;//链表数据
	struct List *next;//下一个节点的地址
}List, *LinkList;

int temp;//记录创建了多少个节点

struct List *Tjd()
{
	struct List *head, *p1, *p2;
	p1 = p2 = (struct List *)malloc(sizeof(struct List));
	//为p1,p2开辟内存空间
	scanf("%d", &p1->data);
	/*对头节点输入数据
	需要注意的是:
	1.头指针和头节点的区别*/
	head = p1;
	/*此时head保存的是第一个节点的数据由于
	head没有数据区,因此他只负责保存第一个
	节点地址根据指针的基础知识得知知道了第
	一个节点的地址在根据每个节点的特性data
	保存实际数据*next保存的是下一个节点内存
	地址就可以依次读取整个链表的内容。*/


	while (p1->data != 0)
		/*
		为什么需要这个语句?
		因为你创建节点时达到要求后需要停止创
		建就需要一个结束命令为了方便就才用了
		判断最后一次输入数据是否等于0来实现*/
	{
		p2 = p1;
		/*执行到这里时已经创建了两个节点p1,p2
		*/
		p1 = (struct List*)malloc(sizeof(struct List));
		/*这句代码的作用是创建一个新的并且是
		空的节点
		疑问1:为什么又要为p1开辟新空间?p1不是
		已经保存了数据了吗?
		解释:因为根据上面代码中的head = p1,p2 = p1可以看出p1已经赋给了两个变量了。重新为
		它开辟新空间将它原有数据覆盖然后在
		经过输入语句向它输入新的数据这样就
		又开辟了一个有数据的新节点
		*/
		scanf("%d", &p1->data);
		/*向上一条代码所创建的新节点输入新数 据*/

		p2->next = p1;
		/*此时p2中next中保存的是p1的地址通过读取p2->next可以得到p1*/

		temp++;
		/*执行完这条代码后程序又回到34行继续执行,直到括号中条件不成立*/
	}

	p2->next = NULL;
	/*这条代码主要作用是使最后一个节点的
	next为空,不指向任何数据*/

	return head;

}

/* =================查询链表中的元素=================*/
int GetElem(LinkList L, int i, int *math)
//第一项为头节点,依次为查询的数值,查得后的数据
{
	LinkList p;
	int j = 1;

	for (p = L->next; j < i - 1; ++j)
		//i-1的目的是使节点序列从1开始
	{
		p = p->next;
		//每次循环是p指向下一个节点指针域
	}

	if (!p || j > i)
		/*判断p的值是否为真或者j是否大于需要
		查询的序列。*/
		return ERROR;

	*math = p->data;/*把最终p所指向的节点的d
					ata项赋给math*/
	return OK;
}

/*=======向链表中指定位置插入一个节点=======*/
int ListInsert(LinkList L, int i, int math)
/*i为插入的位置,且是从1开始*/
{
	int j = 1;
	LinkList L1, p;

	for (p = L; j < i - 1; ++j)
	{
		p = p->next;//使p指向i个节点
	}

	if (!p || j > i - 1)
		//判断插入位置是否合法
	{
		return ERROR;
	}


	L1 = (LinkList)malloc(sizeof(List));
	//创建一个新节点用来插入
	L1->data = math;
	//为新节点的数据项赋值

	L1->next = p->next;
	/*需要理解 p->next意味着什么?
	对于一个完整且合法的链表中p->next中
	保存的是下一个节点的地址,然后把它
	赋给L1->next就意味着把它指向了p->next
	*/
	/*
	疑问2.根据书本上的示例分析可以得出
	一种看似可行的方法?如下:
	以p1  p2表示,新节点用s表示
	p1->next = s;
	s->next = p2;
	解答:看上去逻辑上似乎没问题,其实仔细
	推敲后发展和上面的代码逻辑是一样的只
	不过实现的方式不同,后者有太大的局限
	只能在知道需要插入的两个节点的位置才
	能进行,而前者适用于多种情况下的插入
	*/
	p->next = L1;
	/*
	这条代码根据上一条很容易得知p->next中
	保存了L1的地址就意味着p指向了L1

	可以在纸上画出流程图更加容易理解
	*/

	return OK;

}

/*==============删除链表中指定元素=================*/
int ListDelete(LinkList L, int i, int *math)
//此函数参数与查询函数相似
{
	LinkList p, q;
	int j = 1;

	for (p = L; j < i - 1; ++j)
	{
		p = p->next;
	}
	/*这一步没什么疑问同上都是使p指向指定
	位置i*/

	if (NULL == p->next || j > i - 1)
		//同ListInsert函数中一样
		return ERROR;

	q = p->next;
	p->next = q->next;
	/*
	q = p->next是把p的指针域(下一个节点的地址
	)赋给q。此时q和下一个节点一样两个节点
	都是存在的比如说:p指向的那个节点的data
	项=10,next项是它保存的下一个节点的地址
	经过q=p->next后q就和它相同了同样有了两个
	数据项并且值相同。
	然后,在经过第二条代码后。把q->next中保
	存的下一个节点的地址赋给了p->next这样后
	p就指向了第三个节点。就把第二节点排除
	在链队外了。
	*/
	*math = q->data;
	free(q);
	/*
	把删除的那个节点中的data项赋给math返
	回给主函数中指定变量,然后释放删除的
	这个节点。
	*/
	return OK;
}

/*=========逆序位建立带头节点的链表=========*/
int CreateList(LinkList L, int n)
//n为需要创建的节点数
{
	LinkList p;
	int i;
	L = (LinkList)malloc(sizeof(LinkList));

	L->next = NULL;
	/*存在的主要目的是建立一个空的头节点
	*/

	for (i = n; i > 0; --i)
	{
		p = (LinkList)malloc(sizeof(LinkList));
		scanf("%d", &p->data);

		p->next = L->next;
		L->next = p;
		/*
		这两条代码是此函数的核心所在。第一
		条代码主要目的是使新建的p节点同时
		指向一个节点当执行第一次循环是p的
		next项和L的next相等都是NULL,执行第
		二条代码后L就指向了p,第二次循环时
		又新建了一个节点看为p1此时执行第一
		条代码把L->next赋给p->next,逐步分析
		L->next中保存的是p所以此时p1->next指向
		了p并且p变换到了p1的后面,接着执行
		第二条代码L->next = p此时的p便于理解我
		们看成之前的p1即可,之前的L->next是指
		向p的而不是p1,经过第一条代码p跑到
		了p1的后面所以之前L指向p的连接带可
		视为断裂了(实际没有这回事)为了形成
		完整链表就需要使L指向新建的节点也
		就是L->next = p1。第三次循环,第四次,
		五次都是一会事。只不过变成p2,p3了。
		*/

		++temp;
	}

	for (L; L != NULL; L = L->next)
		printf("我是逆序位:%d\n", L->data);

	return OK;

}

/*===================链表的归并排序===================*/
LinkList  MergeList(LinkList L1, LinkList L2, LinkList L3)
//将两个有序链表归并为一个有序链表L3
{
	LinkList pa, pb, pc, temp;

	pa = L1->next;
	pb = L2->next;
	L3 = pc = L1;//将L1的头节点当做L3的头节点

	while (pa &&  pb)
	{
		if (pa->data <= pb->data)
		{
			pc->next = pa;
			pc = pc->next;
			pa = pa->next;
			/*
			同样是三句核心代码
			语言表达无法解释清楚,大致就是:第一
			条代码是pc->pa;第二条代码中pc->next中
			保存的是pa的地址,而把它赋给pc这样
			pc->data和next都和pa一样。第三天条代
			码把pa->next中保存pa1的地址赋给pa此时
			pa就变成了pa1

			画图更加形象
			*/
		}

		else
		{
			pc->next = pb;
			pc = pc->next;
			pb = pb->next;
			/*
			同上
			*/
		}
	}

	if (NULL != pa)
		pc->next = pa;
	else if (NULL != pb)
		pc->next = pb;
	/*
	这个if...else组合只要是把L1和L2剩下的元素添加进L3中
	*/
	free(L2);

	return L3;
}

int main(void)
{
	LinkList L, L1, L2, L3;

	int math, math1;

	L1 = Tjd();
	//L2 = Tjd();
	L = NULL;
	//L = MergeList(L1, L2, L3);
	/*CreateList(L, 5);此函数可用*/
	GetElem(L1, 3, &math);
	ListInsert(L1, 4, 30);
	ListDelete(L1, 2, &math1);

	printf("我是节点数:%d\n", temp);
	printf("我是查询的数:%d\n", math);

	for (L1; L1 != NULL; L1 = L1->next)
	{
		printf("data: %d\n内存地址: %O\n", L1->data, L1->next);
	}
	return OK;
}

望大神指点迷津
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值