小叙
正所谓璞玉不琢不精,把自己比作璞玉真是老脸也不要了,哈哈。学习C语言后也闲置了几年,如今决定学习数据结构,想起以前学习数据结构课程时老师说多做练习,可惜了那么多的青春被自己开心的浪去了。为了能够记录自己学习数据结构的过程,所以开此博客来督促自己不要又偷懒了,也希望能够得到网上大牛的指导,如果我能帮助到大家,那也是一件极好的事情。好了,闲述太多了,进入正题。
学习数据结构当然就是从最基本的数据结构开始学习,最基本的数据结构包括表、队列和栈。今天总结了一下表的学习。
为什么不用数组来实现表
- 在使用数组之前,数组的大小要提前指定,当事前无法对表的大小进行估算的时候,势必需要定义一个很大的数组,防止溢出,因此这就可能会浪费大量的内存空间。
- 对表进行插值或删除操作时,所有插值或删除位置后的数据都需要进行移位,时间成本的开销就避免不了,最坏的情况是 O(N) .
- 如果采用插值的方式来创建一个列表,显然时间的开销是很大的。
总之因为插入和删除的运行时间是如此的慢以及表的大小必须事先已知,所以简单的数组一般不会用来实现表这种数据结构。
链表
为了避免插入和删除过程中出现的时间线性开销,我们需要允许表可以不连续的存储,否则表的部分或全部需要整体移动。链表是针对以上存在的问题的解决方案。链表由一系列不必在内存中相连的结构组成,每个结构均含有表元素和指向包含该元素后继元素的结构的指针,我们将这个指针称为Next指针(双向链表应该会再有一个前向的指针)。最后一个单元中的Next指向空指针NULL。
前面说了那么多,当然还是码代码加注释最直接啦。
头文件
//在头文件内定义表和操作函数的原型
#ifndef _List_H
struct Node; //声明有一个表节点单元的结构体
typedef int ElementType; //将结构体内的成员类型重定义,这样就可以修改这一处地方就可以按照需要改变数据类型
typedef struct Node *PtrToNode; //重定义指向Node类型的指针类型,方便后面的类型定义
typedef PtrToNode List; //相当于 Node *List 一般用来指向表头位置
typedef PtrToNode Position; //相当于 Node *Position 用来指向表中指定的位置
List MakeEmpty(List L); //将一个链表清空成空表
int IsEmpty(List L); //判断表是否为空
int IsLast(Position P,List L); //判断P指向的位置是否为链表的末尾
Position Find(ElementType X,List L); //返回链表中元素X所在的位置
void Delete(ElementType X,List L); //删除链表中X元素
Position FindPrevious(ElementType X,List L); //找到元素X前一个单元的位置
void Insert(ElementType X,List L,Position P); //在P指向的位置后边插入一个元素X
void DeleteList(List L); //删除链表,并回收删除掉的单元,剩下一个空表
//Position Header(List L);
//Position First(List L);
//Position Advance(Position P);
//ElementType Retrieve(Position P);
#endif // _List_H
源文件
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "list.h" //将头文件包含进来
struct Node
{
ElementType Element;
Position Next; // Node *Next
}; //定义了Node节点的结构,包括成员元素和指向下一个节点的Next指针
定义过数据结构后就是定义数据结构相关的操作
完整的源代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "list.h"
struct Node
{
ElementType Element;
Position Next;
};
List MakeEmpty(List L)
{
L->Next = NULL;
return L;
}
int IsEmpty(List L)
{
return L->Next == NULL;
}
int IsLast(Position P,List L)
{
return P->Next == NULL;
}
Position Find(ElementType X, List L)
{
Position P;
P = L->Next;
while(P != NULL && P->Element != X)
P = P->Next;
return P;
}
Position FindPrevious(ElementType X, List L)
{
Position P;
P = L;
while(P->Next != NULL && P->Next->Element != X)
P = P->Next;
return P;
}
void Delete(ElementType X, List L)
{
Position P,TmpNode;
P = FindPrevious(X,L);
if(!IsLast(P,L))
{
TmpNode = P->Next;
P->Next = TmpNode ->Next;
free(TmpNode);
}
//P->Next = P->Next->Next;
//free(P->Next);
}
void Insert(ElementType X, List L, Position P)
{
Position TmpNode;
TmpNode = malloc(sizeof(struct Node));
TmpNode->Element = X;
TmpNode->Next = P->Next;
P->Next = TmpNode;
}
void DeleteList(List L)
{
Position P,TmpNode;
P = L->Next;
L->Next = NULL;
while(P->Next != NULL)
{
TmpNode = P->Next;
free(P);
P = TmpNode;
}
}
int main()
{
printf("Hello world!\n");
List myList = malloc(sizeof(struct Node));
myList = MakeEmpty(myList);
Position p;
p = myList;
myList->Element = 0;
int i;
for(i = 1; i<5;i++)
{
Insert(i,myList,p);
p = p->Next;
}
printf("list->element = %d\n",myList->Element);
printf("List->Next->Element = %d\n",myList->Next->Element);
p = Find(3,myList);
printf("find 3 in myList = %d\n",p->Element);
p = FindPrevious(3,myList);
printf("previous element of 3 = %d\n",p->Element);
Delete(2,myList);
p = Find(2,myList);
//printf("debug = %d\n",p->Element);
if(p == NULL)
printf("cant find element 2 in myList.\n");
p = Find(4,myList);
if(IsLast(p,myList))
printf("position of element 4 is last of myList.\n");
DeleteList(myList);
if(IsEmpty(myList))
printf("myList has been deleted!\n");
return 0;
}
最终的输出如下:
说明**
需要说明的是在使用链表的时候要首先动态申请一段内存,不然定义的myList可能指向的是重要的数据,会有可能发生意想不到的问题!!!
对于函数的定义都很简单,没有太多需要解释的,但是在我自己写Delete函数的时候却发生函数不能正常运行下去的问题。将代码特别贴出以作说明:
void Delete(ElementType X, List L)
{
Position P,TmpNode;
P = FindPrevious(X,L);
if(!IsLast(P,L))
{
TmpNode = P->Next;
P->Next = TmpNode ->Next;
free(TmpNode);
}
//P->Next = P->Next->Next;
//free(P->Next);
}
可见之间使用free(P->Next);这就话不能正常运行程序,而必须通过一个暂态量TmpNode来删除,这其中的原因在什么地方呢?其实在学习数据结构时养成一个时时画图来理解的习惯是很好的。
从图中可以很清楚的看到 ,如果不定义一个中间变量TmpNode,那么直接执行free(P->Next);就会将X元素后一个单元给删除,而且链表也断了,从而程序也就崩溃了。对于这种低级错误,我想也就我这种刚学习的菜鸟才会遇到,但也希望在这里可以给自己一个教训,也给可能看到这段文字的朋友一点提示,学习数据结构时常画画图,会避免很多问题。
由于是刚学习数据,也就权当是自己的笔记,在总结的过程中必然会有错误的地方,还希望各位朋友能够指出,欢迎大家交流^_^
PS
第一次正儿八经的编辑博客,排版自己也觉得太LOW了,哈哈!