鉴于个人在数据结构的学习中经常遗忘一些重要概念,因此决定在博客中写下自己数据结构的学习历程和心得。
1.线性表的定义
- 线性表(Linear List)是最常用而且最简单的一种数据结构,一个线性表是n个元素的有限序列。表中元素的数据类型需要具体情况具体定义。
- 假如线性表中的元素排列是( a1 , a2 , …… , an ),则对于元素ai( i > 1 && i < n ),必有ai-1作为前驱,ai+1作为后继。表头元素只有后继,表尾元素只有前驱。
2.线性表基本操作的实现
- 由于线性表有两种存储结构,因此在这先写下操作名称,在后文不同的存储方式中再分别写下具体实现操作。
init_linearlist( &L ); //创建一个空线性表
init_destroylist( &L ); //释放线性表内存
list_empty( L ); //判断线性表是否为空表,若为空表则返回false,否则返回true
list_length( L ); //求线性表的长度(即数据元素个数)
list_clear( &L ); //将线性表清空
list_element_locate( L , e , cmp ); //查找线性表中的元素,返回符合条件的元素的位序,其中cmp是数据元素判定函数,相当于条件
list_getelem( L , i ); //返回线性表中第i个元素的值
list_insert( &L , i , e ); //把数据元素e插入线性表的第i个位置
list_delete( &L , x ); //该函数有两种形式,一种是按元素位序删除,一种是按值删除。看需求决定是否返回元素值
list_prior( L . x );
list_next( L, x ); //返回表中某元素的前驱/后继。这里同样有两种形式,一种是按元素位序,另一种是按值返回
- 线性表的顺序存储
typedef int ElementType; typedef struct { ElementType *element; int length; int listsize; }List; void init_linearlist ( List &L ){ cin >> L.listsize; element = new ElementType[L.listsize]; //需要在程序最后用释放 L.length = 0; }
接下来是线性表的基本操作
ElementType getelem( List L , int i ){ return L.element[i-1]; } ElementType list_element_locate( List L , int e ){ for( int i = 0;i < L.length; i++ ){ if(L.element[i] == e)return i; } return 0; } void list_insert( List &L , int i , int e ){ int temp; L.element[L.length++] = e; for( int j = L.length;j > i;j-- ){ temp = L.element[j]; L.element[j] = L.element[j-1]; L.element[j-1] = temp; } } void list_delete1( List &L , int x ){ int temp; for ( int i = L.length;i > x;i-- ){ temp = L.element[i]; L.element[i] = L.element[i-1]; L.element[i-1] = temp; } L.length--; } void list_delete2( List &L , int x ){ int temp; for( int i = L.length;i > list_element_locate(L,x);i-- ){ temp = L.element[i]; L.element[i] = L.element[i-1]; L.element[i-1] = temp; } L.length--; } //这里给出了两种删除操作,第一种是按位序,第二种是按值删除
假设利用两个线性表来表示集合A和B,如何利用线性表的操作来得到一个新集合A∪B呢?
void list_union( List &La , List Lb ){ for( int i = 0;i < Lb.length;i++ ){ int elem = getelem(Lb,i); if(!list_element_locate(La,e))list_insert(La,++La.length,elem); } }
线性表顺序存储的操作就写到这里,相信读者们都可以看出这种方式的缺点。上述代码中不少函数执行效率都直接与表长挂钩,例如插入删除等操作,倘若表长很大,但相应操作只能逐位进行。则函数list_insert和list_delete1的时间复杂度为O(L.length),但是list_delete2的时间复杂度为O( L.length^2 ),接下来讲述线性表的链式存储。
- 线性表的链式存储
typedef int ElementType; typedef struct Lnode { ElementType data; struct Lnode *next; }*List; void init_linearlist( List &L ){ L = new List; L->next = NULL; }
基本操作是对单链表进行遍历,修改节点。
ElementType getelem( List L , int i ){ List p = L;int j = 0; while( p && j < i ){ p = p->next; j++; } if( !p || j > i )return 0; //不存在该元素 return p->data; } ElementType list_insert( List &L , int i , ElementType e ){ List p = L;int j = 0; while( p && j < i ){ p = p->next; j++; } if( !p || j > i )return 0; //i小于1或者大于表长 insertNode = new List; insertNode->data = e; insertNode->next = p->next; p->next = insertNode; } ElementType list_delete( List &L , int i ){ List p = L;int j = 0; while( p && j < i-1 ){ p = p->next; j++; } if( !p || j > i-1 )return 0; //同上 List q = p->next; p->next = q->next; delete q; }今天先写到这里,下次更新一元多项式的加法。