狐狸梦见乌鸦

当坚持成为一种习惯,目标将不在遥远```
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

线性表和单链表

Posted on 2013-01-23 16:44  灬啊U  阅读(581)  评论(0编辑  收藏  举报

 线性表
 线性表:0个或多个数据元素的有限序列。首先,它是一个序列。即,元素之间是有顺序的,若元素存在多个,则第一个元素无前驱,最后一个元素无后继,其他每个元素都由前驱和后继。线性表还强调是有限的。事实上,在计算机中处理的对象都是有限的,那种无限的数列,只存在数学的概念中。
 5.1 线性表的顺序存储结构
 为了建立一个线性表,必须在内存中找到一块地,这块地的第一个位置非常关键,它是存储空间的起始位置。
 顺序存储结构需要3个属性:①存储空间的起始位置;②线性表最大存储容量;③线性表的当前长度;
 数组的长度是:存放线性表的存储空间的长度。
 线性表的长度:是线性表中数据元素的个数,随着线性表插入和删除操作的进行,而变化。
 
 5.2 插入算法的思路:
 ①如果插入位置不合理,返回错误
 ②如果线性表的长度大于等于数组长度,则抛出异常或动态增加容量。
 ③从最后一个元素开始向前遍历到第pos个位置,分别将他们都向后移动一个位置。
 ④将要插入的元素填入位置pos处。
 ⑤表长 加1
 
 5.3 删除算法的思路:
 ①删除位置合理
 ②取出删除元素
 ③从删除位置遍历到最后一个位置,分别将他们都向前移动一个位置
 ④表长 减1

 线性表存储结构的优缺点:
 优点:无须为表示表中元素之间的逻辑关系而增加额外的存储空间。可以快速地存取表中任意位置的元素。
 缺点:插入、删除需要移动大量的元素。当线性表长度变化较大时,难以确定存储空间的容量。造成存储空间的碎片。

 

5.4 线性表的链式存储结构
 5.4.1 顺序存储结构不足的解决方法
 顺序存储的缺点:插入、删除时需要移动大量的元素。为什么需要移动大量的元素呢?原因在于相邻两元素的存储位置也具有邻居关系,

它们在内存中的位置是挨着的,中间没有空隙。 
 线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素。这组单元可以是连续的,也可以是不连续的。即这些元素

可以存在内存未被占用的任意位置。
 链式结构中,每个元素除了存储数据元素信息外,还要存储它的后继元素的存储地址。存数数据元素信息的域称为数据域,把存储直接后

继位置的域称为指针域。指针域中存储的信息称为指针或链。
 因为此链表的每个结点只包含一个指针域,所以叫单链表。链表中第一个结点的存储位置叫做头指针。有时为了更加方便的对链表进行操

作,会在单链表的第一个结点前附设一个头结点。头结点的数据域可以不存储任何信息,头结点的指针域存储指向第一个结点的指针。
 5.4.2 头指针和头结点的异同
 头指针:链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。头指针具有标识作用,所以常用头指针冠以链表的名字

。无论链表是否为空,头指针均不为空。头指针是链表的必要元素。
 头结点:是为了操作的同一方便而设立的,放在第一个元素结点之前,其数据域一般无意义(也可存放链表的长度)。有了头结点,对在

第一个元素结点前插入结点和删除第一结点,其操作与其它结点的操作就同一了,头结点不一定是链表必须要素。

单链表的结构体:

1 typedef int datatype;
2 
3 typedef struct node
4 {
5       datatype data;       //数据域
6       struct node *next;  //指针域
7 }linklist;
8 struct node *linklist;

5.4.3 单链表的插入结点算法:
 ①声明一个结点p指向链表第一个结点,初始化计数器j从1开始。
 ②当计数器j<i时,遍历链表,让p的指针向后移动,不断指向下一个结点,计数器j累加1
 ③若到链表末尾p为空,则说明第i个结点不存在
 ④否则查找成功,在系统中生成一个空结点s
 ⑤将数据元素e赋值给s->data
 ⑥插入链表中 s->next = p->next; p->next = s;
 ⑦返回成功

 1 int  InsertLinkList(linklist h, int pos, datatype x)
 2 {
 3     linklist p;
 4     if ((pos < 0) || (pos > LengthLinkList(h)))
 5         return -1;
 6     while (pos--)
 7     {
 8         h = h->next;
 9     }
10     p = (linklist)malloc(sizeof(linknode));
11     p->data = x;
12     p->next = h->next;
13     h->next = p;
14     return 0;
15 }

头插法:

 1 int InsertLinkListHead(liinlist h, int pos, datatype x)
 2 {
 3     int count = 0;
 4     linklist p, s;
 5     p = h;
 6     
 7     while (p && count < pos)
 8     {
 9         p = p->next;
10         count++;
11     }
12     if (!p || count > pos)
13         return -1;
14     s = (linklist)malloc(sizeof(linkmode));
15     s->data = x;
16     s->next = p->next;
17     p->next = s;
18     return 0;
19 }

 5.4.4 单链表的读取操作
 单链表中要获取第i个元素,必须从头开始找。
 算法:
 ①声明一个结点p指向链表的第一个结点,初始化j从1开始
 ②当j < i时,就遍历链表,让p的指针向后移动,不断指向下一个结点,j累加1
 ③若到链表末尾p为空,则说明第i元素不存在
 ④否则查找成功,返回结点p的数据。

 5.4.5 单链表的删除算法:
 ①声明一个结点p指向链表的第一个结点,计数器j从1开始计数
 ②当j<i时,就遍历链表,让p的指针向后移动,不断指向下一个结点,j累加
 ③若到链表末尾p为空,则说明第i个元素结点不存在
 ④否则查找成功,将欲删除的结点p->next 赋值给q
 ⑤单链表删除p->next = q->next
 ⑥将q结点的数据值赋给e,作为返回
 ⑦释放q结点
 ⑧返回成功
 

 1 int DeleteLinkList(linklist h, int pos)
 2 {
 3     linklist p;
 4     if ((pos < 0) || (pos > LengthLinkList(h)))
 5         return -1;
 6     while (pos--)      //找到要删除节点的前一结点
 7         h = h->next;
 8     p = h->next;     //p为要删除的结点
 9     h->next = p->next;
10     free(p);
11     return 0;
12 }
13 
14 int DeleteLinkListTwo(linklist h, int pos)
15 {
16     int count = 0;
17     linklist p, q;
18     p = h;
19     
20     while (!(p->next) || count < pos)
21     {
22         p = p->next;
23         count++;
24     }
25     if (!(p->next) || count > pos)
26         return -1;
27     q = p->next;
28     p->next = q->next; //p->next = p->next->next;
29     free(q);
30     return 0;
31 }

分析总结:单链表的插入、删除操作都由2部分组成,第一:遍历查找第i个元素;第二:插入、删除元素。时间复杂度都为O(n)。
 
 5.5 单链表的整表创建
 顺序存储结构的创建,实际上就是一个数组的初始化,即声明一个类型和大小的数组并赋值的过程。而单链表与之不同,对于每个链表来说,它所占用空间的大小和位置不需要预先分配规定,可根据实际情况需求即时生成。因此,创建单链表的过程就是一个动态生成链表的过程。即从空表的初始状态,依次建立各元素结点,并逐个插入链表。
 算法:
 ①声明一个结点p和计数器变量i
 ②初始化以空链表L
 ③让L的头结点的指针指向null,即建立一个带头结点的单链表
 ④循环
  1、生成一个新结点赋值给p
  2、随机生成一个数字赋值给p的数据域p->data
  3、将p插入到头结点与前一新结点之间

 5.6 单链表的整表删除
 算法:
 ①声明2个结点p和q
 ②将第一个结点赋值给p
 ③循环
  1、将下一个结点赋值给q
  2、释放p
  3、将q赋值给p

 1 int ClearLinkList(linklist h)
 2 {
 3     linklist p, q;
 4     p = h->next;                //point to fisrt node
 5     
 6     while (p != NULL)          // not to end
 7     {
 8         q = p->next;      
 9         free(q);
10         p = q;
11     }
12     h->next = NULL;          //head node point NULL
13 }

5.7 单链表结构和顺序存储结构优缺点
 1、存储分配方式
      ①顺序存储结构用一段连续的存储单元一次存储线性表的数据元素
      ②单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素
 2、时间性能
      ①查找:
      顺序存储结构O(1)
      链式存储结构O(n)
      ②插入、删除
      顺序存储平均移动表长一半的元素,时间为O(n)
      链式存储在找出某位置的指针后,插入和删除时时间仅为O(1)
 3、空间复杂度
      ①顺序存储结构需要预分配存储空间,分大了,浪费,分少了容易发生溢出
      ②链式存储结构不需要分配存储空间,只要有就可以分配,元素个数也不受限制。 
 
 因此:若线性表需要频繁查找,很少进行插入和删除操作时,应该采用顺序存储结构。若需要频繁插入和删除时,应该采用链式存储结构。例如:游戏开发中,对于用户注册的个人信息,除了注册时插入数据外,绝代多数情况都是读取,所以应该考虑用顺序存储结构。而游戏中玩家的武器或者装备列表,随着玩家的游戏过程中,可能会随时增加或删除,此时应该选择单链表结构。
 当线性表中的元素个数变化较大或者根本不知道有多大时,最好用单链表结构,这样可以不需要考虑存储空间大小的问题。如果事先知道

线性表的大致长度,比如,一年12个月,一周7天,这种用顺序存储结构效率会高很多。
 
 总之:线性表的顺序存储结构和单链表结构各有优缺点,不能简单的说哪个好坏,需要根据实际情况,来综合衡量。【2013-1-23 16:46】