<线性结构>笔记目录
线性表的实现与多项式表示 http://www.zsenhe.com/article/83 堆栈与表达式求值问题 http://www.zsenhe.com/article/84 队列,顺环队列 http://www.zsenhe.com/article/85
引子——多项式表示 线性结构是数据结构里最基础,最简单的一种类型;其中最典型的便是“线性表”,什么是线性表呢? 考虑一下一元多项式的表示: 这样的多项式基本运算包括: 两个多项式相加,相乘,相减等;那么,怎么用程序设计语言来表示这样一个多项式及运算呢?在程序设计语言里面,要表示一个问题,首先要分析一下问题的关键数据和关键信息;对于多项式而言,它的关键信息主要有它的项数和最高指数。 其中一种最简单的方法便是 顺序存储的直接表示 直接使用一个数组来表示多项式的有关信息,数组各分量对应多项式各项
a[i] 项 x^i 的系数;i为对应的指数
比方对于下面这个多项式: 表示为: 这样用6个分量来表示该多项式;这样的表示是很简单的,且运算也方便 如对于两个多项式的相加,即变成了 两个数组对应分量的相加 但该表示方式也是有问题的,如果我们要存储 那么需要2001长度的数组来表示,而这2001个分量,只有两项是非0的,于是便造成了巨大的空间浪费;且运算的时候会多一些没必要的操作(大量的+0) 有没有可能只表示非0项呢?我们可以将多项式看成一个由 系数和指数 组成的二元组集合,使用结构数组来表示这样的多项式 对于前面的x+2x^2000,只需要使用两个分量来存储;且同样运算很方便,其中要点是每一项按指数大小降序存储 例如加法运算,图中的两个多项式,他们用分量表示为 p1:
(9,12) (15,8) (3,2)
p2:
(26,19) (-4,8) (-13,6) (82,0)
从头开始,比较两个多项式当前指数较大的那一项,例如第一项选择 (9,12) 与(26,19),前面提过我们需要按照指数大小降序排放,那么选择较大那一项输出(26,19),接下来(9,12)与(-4,8)比较,显然输出(9,12) ;接下来比较的两个分量指数相同,系数相减,输出和(11,8),接下来的比较输出(-13,6) (3,2);最后是p2的剩余项(82,0) 最后得到新的多项式 对比一下,我们使用结构体的形式存储在数组中表示非0项,这是一种节省空间的方法 当然,我们也可以使用一种其他形式存储多项式 链表 它的表示方法为: 每个节点存储多项式中的一个非0项,包括系数和指数两个数据域以及一个指针域
1 2 3 4 5 6 typedef struct PolyNode *Polynomial; struct PolyNode { int coef; //系数 int expon; //指数 Polynomial link; //指向下一项 }
前面的两个多项式,他们以链表的形式存储状态为: 运算的逻辑过程与数组表现的形式一样
线性表 由多项式的问题得知: 同一个问题可以有不同的表现方法,也就是不同的存储方法;对于多项式的表示,要么使用数组要么使用链表。实际上很多问题与多项式表示是有共性的,我们的最终目标都是:管理,组织一个有序的线性序列,归结为线性表问题
什么是线性表?
由同类型数据元素构成有序序列的线性结构: 表中元素个数称为线性表的 长度 线性表没有元素时称为 空表 表起始位置称 表头 ,表结束位置称 表尾
对线性表来说,它的抽象数据类型描述为:
线性表的存储 学习数据结构时我们最关心的是它的存储,对于线性表的存储其中最简单的一种方式便是顺序存储,即使用数组的方法实现 这种方式需要关心的是线性表的 数据类型 以及 长度
1 2 3 4 5 struct LNode { ElementType data[MAXSIZE]; int last; } struct LNode L ;
使用指针last 代表线性表的最后一个元素,访问长度时执行 L.last+1 这样的结构就可以抽象的实现线性表,比如说当想要访问下标为i的元素时使用 L.Data[i]
主要操作的实现 为了方便,我们使用指针来传递线性表
1 2 3 4 5 6 7 8 #include <iostream> using namespace std ; const int MAXSIZE = 1000 ;struct LNode { char data[MAXSIZE]; int last; };typedef LNode *List;
1.初始化操作
1 2 3 4 5 6 List MakeEmpty () { List Ptrl; Ptrl = (List )malloc (sizeof (struct LNode)); Ptrl->last = -1 ; return Ptrl; }
注意初始化时表长度为-1,即表示数组长度为02.查找操作
1 2 3 4 5 6 int Find (char x,List Ptrl) { int i=0 ; while (i<=Ptrl->last&&Ptrl->data[i]!=x) i++; if (i>Ptrl->last) return -1 ; return i; }
从0开始遍历表,当while循环退出时,要么i==ptrl->last+1要么data[i]=x,这两种情况分别对应着 未寻找到元素 和 找到元素 它的时间复杂度显然为O(n) 3.插入 插入的目的是在线性表的第i个位置上插入一个新的元素,传入的i指的是[1,last+1] (1即是表头,last+1表尾);所以实际上我们要把元素插入到i-1的位置上 那么首先要做的就是把i-1之后的所有元素往后挪动一位,我们用一个循环来做这件事
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void Insert (char x,int i,List Ptrl) { if (Ptrl->last==MAXSIZE-1 ){ printf ("full" ); return ; } if (i<1 ||i>Ptrl->last+2 ){ printf ("位置不合法" ); return ; } for (int j=Ptrl->last;j>=i-1 ;j--) Ptrl->data[j+1 ] = Ptrl->data[j]; Ptrl->data[i-1 ] = x; Ptrl->last++; return ; }
该操作的时间复杂度依然是O(n)4.删除元素 我们要把第i-1个元素移出,那么实际上要做的把i及之后的元素全部往前挪,这个过程按从左往右的顺序做
1 2 3 4 5 6 7 void Delete (int i,List Ptrl) { if (i<1 ||i>Ptrl->last+1 ) return ; for (int j=i;j<=Ptrl->last;j++) Ptrl->data[j-1 ] = Ptrl->data[j]; Ptrl->last--; return ; }
链式存储 线性表除了用数组实现,同样可以使用链表来组织 线性表使用数组实现时,两个相邻的元素不止逻辑上是相邻的,在物理上也是相邻的(数组在内存中也是连续存放的一片空间);而我们使用链表实现时,实际上是要求两个元素只在逻辑上相邻(通过“链”建立),而不要求物理上相邻 这样的好处是进行插入,删除操作时,只需要修改“链”的指向,而不需要像数组实现那样移动整个空间,这会节省很多时间
1 2 3 4 5 6 7 #include <iostream> using namespace std ; typedef struct LNode *List ;struct LNode { char data; List next; };
如图,链表中每个节点拥有两个分量,Data是节点所对应的数据,Next指向下一个节点
访问序号为i的元素?求线性表的长度? 上文提到链实现线性表在插入删除方面有着数组无可比拟的高效,那除此之外呢?在数组实现中访问序号为i的元素是很简单的事,直接ElelementType[i-1]即可;同样,我们也可以直接取last来知道线性表的长度.但在链表中,这两个操作比起数组实现就复杂了许多求表长
1 2 3 4 5 6 7 8 9 int length (List ptrl) { List p = ptrl; int j = 0 ; while (p){ p = p->next; j++; } return j; }
因为需要遍历整个链表,它的时间复杂度为O(n);在数组实现中该操作是O(1)的按序号访问节点
1 2 3 4 5 6 7 8 9 10 List findKth (int k,List ptrl) { List p = ptrl; int i=1 ; while (!p&&i<k){ p = p->next; i++; } if (i==k) return p; return NULL ; }
当while循环退出时,意味着!p或者i<k这两个条件之一被破坏了,当p不为null时,即找到了目标节点按值查找
1 2 3 4 5 List findKth0 (char target,List ptrl) { List p = ptrl; while (!p&&p->data!=target) p = p->next; return p; }
插入与删除 插入 将数据插入到i的位置上,意味着我们要将数据插入到i-1的后面,为了完成这个操作 我们需要进行以下几个步骤的操作
1.申请一片新的内存,使用s指向它 2.找到链表的i-1个节点,使用p指向 3.修改指针,插入节点 修改指针的动作有两步,将s->next指向p->next,再将p->next指向s(注意不要搞反)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 List insert (char x,int i,List ptrl) { List p,s; if (i==1 ){ s = (List)malloc (sizeof (struct LNode)); s->data = x; s->next = ptrl; return s; } p = findKth(i-1 ,ptrl); if (p==NULL ){ return NULL ; } s = (List)malloc (sizeof (struct LNode)); s->data = x; s->next = p->next; p->next = s; return ptrl; }
删除 删除同理是对指针的修改,同样的使用p指向i-1,申请一个新的指针s指向p->next,最后将p->next指向s->next即可;通过malloc申请的空间要记得free回去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 List Delete (int i,List ptrl) { List p,s; if (i==1 ){ s = ptrl; if (ptrl!=NULL ) ptrl = ptrl->next; else return NULL ; free (s); return ptrl; } p = findKth(i-1 ,ptrl); if (p==NULL ){ return NULL ; }else if (p->next==NULL ) return NULL ; s = p->next; p->next = s->next; free (s); return ptrl; }
完整的代码存档:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <iostream> #include <malloc.h> using namespace std ; const int MAXSIZE = 1000 ;struct LNode { char data[MAXSIZE]; int last; };typedef LNode *List; List MakeEmpty () { List Ptrl; Ptrl = (List )malloc (sizeof (struct LNode)); Ptrl->last = -1 ; return Ptrl; } int Find (char x,List Ptrl) { int i=0 ; while (i<=Ptrl->last&&Ptrl->data[i]!=x) i++; if (i>Ptrl->last) return -1 ; return i; } void Insert (char x,int i,List Ptrl) { if (Ptrl->last==MAXSIZE-1 ) return ; if (i<1 ||i>Ptrl->last+2 ) return ; for (int j=Ptrl->last;j>=i-1 ;j--) Ptrl->data[j+1 ] = Ptrl->data[j]; Ptrl->data[i-1 ] = x; Ptrl->last++; return ; } void Delete (int i,List Ptrl) { if (i<1 ||i>Ptrl->last+1 ) return ; for (int j=i;j<=Ptrl->last;j++) Ptrl->data[j-1 ] = Ptrl->data[j]; Ptrl->last--; return ; } int main () { List L = MakeEmpty(); Insert('a' ,1 ,L); cout << Find('b' ,L); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 #include <iostream> #include <malloc.h> using namespace std ; typedef struct LNode *List ;struct LNode { char data; List next; }; int length (List ptrl) { List p = ptrl; int j = 0 ; while (p){ p = p->next; j++; }return j; } List findKth (int k,List ptrl) { List p = ptrl; int i=1 ; while (p&&i<k){ p = p->next; i++; } if (i==k) return p; return NULL ; } List findKth0 (char target,List ptrl) { List p = ptrl; while (p&&p->data!=target) p = p->next; return p; } List insert (char x,int i,List ptrl) { List p,s; if (i==1 ){ s = (List)malloc (sizeof (struct LNode)); s->data = x; s->next = ptrl; return s; } p = findKth(i-1 ,ptrl); if (p==NULL ){ return NULL ; } s = (List)malloc (sizeof (struct LNode)); s->data = x; s->next = p->next; p->next = s; return ptrl; } List Delete (int i,List ptrl) { List p,s; if (i==1 ){ s = ptrl; if (ptrl!=NULL ) ptrl = ptrl->next; else return NULL ; free (s); return ptrl; } p = findKth(i-1 ,ptrl); if (p==NULL ){ return NULL ; }else if (p->next==NULL ) return NULL ; s = p->next; p->next = s->next; free (s); return ptrl; } struct LNode L ;List Ptrl; int main () { return 0 ; }