2.3线性表的链式表示和实现

来源:互联网 发布:安卓6.0读取数据库 编辑:程序博客网 时间:2024/06/06 18:40

算法2.8:单链表中插入一个节点

下面是书中的伪代码:

Status ListInsert_L(LinkList &L, int i, ElemType e) {   // 在带头结点的单链线性表L的第i个元素之前插入元素e  LinkList p,s;  p = L;     int j = 0;  while (p && j < i-1) {  // 寻找第i-1个结点    p = p->next;    ++j;  }   if (!p || j > i-1) return ERROR;      // i小于1或者大于表长  s = (LinkList)malloc(sizeof(LNode));  // 生成新结点  s->data = e;  s->next = p->next;      // 插入L中  p->next = s;  return OK;} // LinstInsert_L

下面我们来分析下:

1.第一行是Status表示一种状态,因为C语言里面没有泛型,所以用这个表示。

2.这里的LinkList是*LinkList(书上对这个有定义,在本博客中不再说明)。

3.这里如果LinkList &L,是一个链表,这个p指向了L的地址。这样的话当while循环结束后,那么p就是指向第i-1个元素的地址。

4.s在堆中开辟了一个LNode大小的空间,并且把e赋值给了s->data。

5.插入时我们要注意,先把p->next给s->next(就是先连s后继,如果先连接p的后继话这个链表是不能连起来的,大家可以试试),然后在连接p的后继。


算法2.9:带头结点的链表的删除

下面是书中的伪代码:

Status ListDelete_L(LinkList &L, int i, ElemType &e) {   // 在带头结点的单链线性表L中,删除第i个元素,并由e返回其值  LinkList p,q;  p = L;  int j = 0;  while (p->next && j < i-1) {  // 寻找第i个结点,并令p指向其前趋    p = p->next;    ++j;  }  if (!(p->next) || j > i-1) return ERROR;  // 删除位置不合理  q = p->next;  p->next = q->next;           // 删除并释放结点  e = q->data;  free(q);  return OK;} // ListDelete_L
下面是思路:

这个程序比较简单,我们讲解下思路就可以了。先用p指向要删除的LinkList的前面那个元素,用q指向p后面那个元素(也就是要删除的元素),把q->next赋值各p->next,这样的化就把q隔离出来了,最后在free就可以了。这个比较简单。


算法2.10:创建带头结点的单链表。

下面是书中的伪代码:

void CreateList_L(LinkList &L, int n) {   // 逆位序输入(随机产生)n个元素的值,建立带表头结点的单链线性表L   LinkList p;  int i;  L = (LinkList)malloc(sizeof(LNode));  L->next = NULL;              // 先建立一个带头结点的单链表  for (i=n; i>0; --i) {    p = (LinkList)malloc(sizeof(LNode));  // 生成新结点    p->data = random(200);     // 改为一个随机生成的数字(200以内)    p->next = L->next;    L->next = p;    // 插入到表头  }} // CreateList_L

下面是思路:

1.带头结点的链表是指,链表里面的第一个结点,不存数据。

2.这里的i是要包含除头结点外的还有多少个节点。

3.这里的random函数是产生随机种子,他在头文件#include<stdlib.h>中,大小为0~199。

4这里是在表头中不停的插入,而不是在表尾中插入。


算法2.11:

如何将两个有序链表并为一个有序链表。

下面是伪代码:

void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc) {  // 已知单链线性表La和Lb的元素按值非递减排列。  // 归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列。  LinkList pa, pb, pc;  pa = La->next;    pb = Lb->next;  Lc = pc = La;             // 用La的头结点作为Lc的头结点  while (pa && pb) {    if (pa->data <= pb->data) {      pc->next = pa;   pc = pa;   pa = pa->next;    }    else { pc->next = pb;   pc = pb;   pb = pb->next; }  }  pc->next = pa ? pa : pb;  // 插入剩余段  free(Lb);                 // 释放Lb的头结点} // MergeList_L

下面我们来分析下:

1.这个程序是思路是,用三个LinkList,pa,pb.pc分别指向La,Lb的第一个元素Lc的头结点,然后比较,pa->data,和pb->data的值,谁下就把谁放入pc,然后指针后移,在最后,把还剩下的那个链的部分,直接接到pc。

2.这伪代码有个问题,也就是最后的free(Lb),如果要清理,也要加上free(La)才行。

3.这个例子的巧妙之处在于只需将原来两个链表中节点之间的关系解除,程序按元素非递减的关系将所有节点链接成一个链表即可。



算法2.12:

在静态链线性表L中查找第1个值为e的元素

下面是伪代码:

int LocateElem_SL(SLinkList S, ElemType e) {   // 在静态单链线性表L中查找第1个值为e的元素。  // 若找到,则返回它在L中的位序,否则返回0。  int i;  i = S[0].cur;                              // i指示表中第一个结点  while (i && S[i].data != e) i = S[i].cur;  // 在表中顺链查找  return i;} // LocateElem_SL
下面我们来分析下:

1.静态链表:在不设置“指针”类型的高级程序设计语言中使用的,需要预先分配一个较大的空间。

2.这个程序的思路是:cur是一个指示器类似于next,如下图所示,相信看了下图,就能理解,下图是插入数据元素SHI和删除数据元素ZHENG



3.这个cur存的是下标(因为这是SLinkList[MAXSIZE])。

4.最后返回数组下标的值。



算法2.13:

在静态链中将所有未被使用过以及被删除的分量用游标链成一个备用的链表,每当进行插入时便可从备用链表上取得第一个结点作为待插入点;反之,在删除时将此元素插入S表

下面以(A-B)U(B-A)=(AUB)-(A∩B)为例子。

假设由终端输入集合元素,先建立表示集合A的静态链表S,从而在输入集合B的元素的同时查找S表,若存在和B相同的元素,则从S表删除,否则插入S表。

算法2.13功能:将整个数组空间初始化成一个链表。

算法2.14功能:从备用空间取得一个结点。

算法2.15功能:将空间节点链结到备用链表上。

算法2.16功能:完整功能实现

下面是伪代码:

void InitSpace_SL(SLinkList space) {  // 将一维数组space中各分量链成一个备用链表,space[0].cur为头指针,  // "0"表示空指针  for (int i=0; i<MAXSIZE-1; ++i)      space[i].cur = i+1;  space[MAXSIZE-1].cur = 0;} // InitSpace_SL
下面来分析下:

1.init的全写为initialization,意思为初始化。

2.这个程序的最后是space[MAXSIZE-1].cur=0;是把这个静态链头尾相连变成一个环。



算法2.14:从备用空间取得一个结点。

下面是代码:

int Malloc_SL(SLinkList &space) {   // 若备用空间链表非空,则返回分配的结点下标,否则返回0  int i = space[0].cur;  if (space[0].cur) space[0].cur = space[space[0].cur].cur;  return i;} // Malloc_SL
下面我们来分析下代码:

1.malloc的全称为mallocate,意思是分配。

2.这个程序是首先得到下标为0的链表对应的cur,当下标为0的cur不为0时,说明非空,则把space[0].cur这个是数提出来,再把这个数组对应的下标赋值给0对应的下标。

3.不懂的同学,带几个数进去就懂了。





算法2.15:将空间节点链结到备用链表上

下面是伪代码:

void Free_SL(SLinkList &space, int k) {  // 将下标为k的空闲结点回收到备用链表  space[k].cur = space[0].cur;    space[0].cur = k;} // Free_SL
下面我们来分析下:

这个是先把以前0的下标给后数组第k行对应的下标,然后到数组0下标改为k。



算法2.16:完整功能实现

下面是伪代码:

void difference(SLinkList &space, int &S) {  // 依次输入集合A和B的元素,在一维数组space中建立表示集合(A-B)∪(B-A)  // 的静态链表, S为头指针。假设备用空间足够大,space[0].cur为头指针。  int i, j, k, m, n, p, r;  ElemType b;  InitSpace_SL(space);          // 初始化备用空间  S = Malloc_SL(space);         // 生成S的头结点  r = S;                        // r指向S的当前最后结点  m = random(2,8);              // 输入A的元素个数  n = random(2,8);              // 输入B的元素个数  printf("  A = ( ");  initrandom_c1();  for (j=1; j<=m; ++j) {        // 建立集合A的链表    i = Malloc_SL(space);      // 分配结点    //printf("i=%d   ",i);    space[i].data = random_next_c1();   // 输入A的元素值    printf("%c ", space[i].data);       // 输出A的元素    space[r].cur = i;  r = i;  // 插入到表尾  }  printf(")\n");  space[r].cur = 0;             // 尾结点的指针为空  initrandom_c1();  printf("  B = ( ");  for (j=1; j<=n; ++j) {    // 依次输入B的元素,若不在当前表中,则插入,否则删除    b = random_next_c1();       // 输入B的元素值    printf("%c ", b);           // 输出B的元素    p = S;   k = space[S].cur;  // k指向集合A中第一个结点    while (k!=space[r].cur && space[k].data!=b) {// 在当前表中查找      p = k;    k = space[k].cur;    }    if (k == space[r].cur) {      // 当前表中不存在该元素,插入在r所指结点之后,且r的位置不变      i = Malloc_SL(space);      space[i].data = b;      space[i].cur = space[r].cur;      space[r].cur = i;    } else {                     // 该元素已在表中,删除之      space[p].cur = space[k].cur;      Free_SL(space, k);      if (r == k)  r = p;      // 若删除的是尾元素,则需修改尾指针    }  }  printf(")\n");} // difference

下面我们来分析一下:

1.这里面用产生了两个随机种子赋值给了m和n。

2.这里的random_next_c1();函数估计就是参数一个随机字符。

3.第一个for循环结束后把space里面下标为1-m的数都赋值了。

4.第二个for循环里面的while循环,是判断是否有相同的数,和是不是检索完了数组。

假设A=(c,b,e,g,f,d),B=(a,b,n,f)则,这图很好的表示了此算法:


1 0