代码可能比较杂乱,严蔚敏书上要求实现的基本都实现了,只不过一些功能需要单独使用。
-------链表的创建、插入、删除、归并排序
希望各位老师给我指出不足
代码如下:
// 线性链表.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;
}
望大神指点迷津