数据结构 学习笔记02——线性表
线性表的定义:具有相同特性的数据元素的一个有限序列。该序列中所含元素的个数叫做线性表长度。
定义:L=(a1,a2,a3....an)
其中为a1表头元素,为an表尾元素
线性表的顺序存储结构
其直接将线性表的逻辑结构映射到存储结构上
***********************************************
例:线性表La,Lb分别表示集合A,B,求A = AUB。
void Union(List &La, List Lb) { // 算法2.1 // 将所有在线性表Lb中但不在La中的数据元素插入到La中 int La_len,Lb_len,i; ElemType e; La_len = ListLength(La); // 求线性表的长度 Lb_len = ListLength(Lb); for (i=1; i<=Lb_len; i++) { GetElem(Lb, i, e); // 取Lb中第i个数据元素赋给e if (!LocateElem(La, e, equal)) // La中不存在和e相同的数据元素 ListInsert(La, ++La_len, e); // 插入 } } // union
例:
void MergeList(List La, List Lb, List &Lc) { // 算法2.2 // 已知线性表La和Lb中的元素按值非递减排列。 // 归并La和Lb得到新的线性表Lc,Lc的元素也按值非递减排列。 int La_len, Lb_len; ElemType ai, bj; int i=1, j=1, k=0; InitList(Lc); La_len = ListLength(La); Lb_len = ListLength(Lb); while ((i <= La_len) && (j <= Lb_len)) { // La和Lb均非空 GetElem(La, i, ai); GetElem(Lb, j, bj); if (ai <= bj) { ListInsert(Lc, ++k, ai); ++i; } else { ListInsert(Lc, ++k, bj); ++j; } } while (i <= La_len) { GetElem(La, i++, ai); ListInsert(Lc, ++k, ai); } while (j <= Lb_len) { GetElem(Lb, j++, bj); ListInsert(Lc, ++k, bj); } } // MergeList
***********************************************
线性表的顺序表示和实现
//---------------------线性表的顺序存储类型描述------------------------
#define MAXSIZE 50 typedef struct { ElemType date[MAXSIZE]; int length; }SqList;
//---------------------线性表的的动态分配顺序存储结构--------------------
#define LIST_INIT_SIZE 100 //线性表存储空间的初始分配量 #define LISTINCREMENT 10 //线性表存储空间的分配增量 typedef struct{ ElemType * elem ; //存储空间基址 int length ; //当前长度 int listsize ; //当前分配存储容量(以sizeof(ElemType)为单位) }SqList;
//-------------------构造一个空的线性表L(以动态的方式)-------------------
Status InitList_Sq(SqList &L) { // 算法2.3 // 构造一个空的线性表L。 L.elem = (ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType)); if (!L.elem) return OK; // 存储分配失败 L.length = 0; // 空表长度为0 L.listsize = LIST_INIT_SIZE; // 初始存储容量 return OK; } // InitList_Sq
常用基本运算集合
初始化
创建
销毁
是否为空
求线性表的长度
输出线性表
求线性表中某个元素的值
按元素值查找
插入运算(一般是前插)
顺序表位序从1开始,因此要注意将逻辑位序转化为物理位序
线性表插入、删除算法时间复杂度为O(n) [注:一般实现]
----------------------------------------------------------------------------------------------------------------
例:有一个顺序表A。设计一个算法,删除所有元素值在[x,y]之间的所有元素,要求算法时间复杂度为O(n),空间复杂度为O(1)
实现:以A表为基础重新构建一个表。
例:有一个顺序表L,假设元素类型ElemType为整型,并且所有元素均不相等。设计一个算法,以第一个元素为分界线,将所有小于它的元素移到该元素前面,将所有大于它的元素移到该元素的后面。
实现1:从两边向中间交替查找不满要求的元素进行交换
实现2:保留第一个元素的值,然后从右边前左边查到不满要求的元素,并将其设置到第一个元素位置 ... ...(变化基准位置(原来第一个元素的位置),从而将缺省出的基准位用于存放找到的数值)
//---------------------在顺序线性表L的第i个元素之前插入新的元素e-------------------------
Status ListInsert_Sq(SqList &L, int i, ElemType e) { // 算法2.4 // 在顺序线性表L的第i个元素之前插入新的元素e, // i的合法值为1≤i≤ListLength_Sq(L)+1 ElemType *p; if (i < 1 || i > L.length+1) return ERROR; // i值不合法 if (L.length >= L.listsize) { // 当前存储空间已满,增加容量 ElemType *newbase = (ElemType *)realloc(L.elem, (L.listsize+LISTINCREMENT)*sizeof (ElemType)); if (!newbase) return ERROR; // 存储分配失败 L.elem = newbase; // 新基址 L.listsize += LISTINCREMENT; // 增加存储容量 } ElemType *q = &(L.elem[i-1]); // q为插入位置 for (p = &(L.elem[L.length-1]); p>=q; --p) *(p+1) = *p; // 插入位置及之后的元素右移 *q = e; // 插入e ++L.length; // 表长增1 return OK; } // ListInsert_Sq
//--------------------在顺序线性表L中删除第i个元素,并用e返回其值-------------------------
Status ListDelete_Sq(SqList &L, int i, ElemType &e) { // 算法2.5 // 在顺序线性表L中删除第i个元素,并用e返回其值。 // i的合法值为1≤i≤ListLength_Sq(L)。 ElemType *p, *q; if (i<1 || i>L.length) return ERROR; // i值不合法 p = &(L.elem[i-1]); // p为被删除元素的位置 e = *p; // 被删除元素的值赋给e q = L.elem+L.length-1; // 表尾元素的位置 for (++p; p<=q; ++p) *(p-1) = *p; // 被删除元素之后的元素左移 --L.length; // 表长减1 return OK; } // ListDelete_Sq
//--------------------在顺序线性表L中查找第1个值与e满足compare()的元素的位序------------
int LocateElem_Sq(SqList L, ElemType e, Status (*compare)(ElemType, ElemType)) { // 算法2.6 // 在顺序线性表L中查找第1个值与e满足compare()的元素的位序。 // 若找到,则返回其在L中的位序,否则返回0。 int i; ElemType *p; i = 1; // i的初值为第1个元素的位序 p = L.elem; // p的初值为第1个元素的存储位置 while (i <= L.length && !(*compare)(*p++, e)) ++i; if (i <= L.length) return i; else return 0; } // LocateElem_Sq
//--------------------已知顺序线性表La和Lb的元素按值非递减排列---------------------------
void MergeList_Sq(SqList La, SqList Lb, SqList &Lc) { // 算法2.7 // 已知顺序线性表La和Lb的元素按值非递减排列。 // 归并La和Lb得到新的顺序线性表Lc,Lc的元素也按值非递减排列。 ElemType *pa,*pb,*pc,*pa_last,*pb_last; pa = La.elem; pb = Lb.elem; Lc.listsize = Lc.length = La.length+Lb.length; pc = Lc.elem = (ElemType *)malloc(Lc.listsize*sizeof(ElemType)); if (!Lc.elem) exit(OVERFLOW); // 存储分配失败 pa_last = La.elem+La.length-1; pb_last = Lb.elem+Lb.length-1; while (pa <= pa_last && pb <= pb_last) { // 归并 if (*pa <= *pb) *pc++ = *pa++; else *pc++ = *pb++; } while (pa <= pa_last) *pc++ = *pa++; // 插入La的剩余元素 while (pb <= pb_last) *pc++ = *pb++; // 插入Lb的剩余元素 } // MergeList
线性表的链式表示和实现
//--------------线性表的单链表存储结构---------------------
typedef struct LNode{ ElemType data; struct LNode *next; }LNode, *LinkList;
对于带头节点的单链表而言
插入结点:s->next = p->next; (插入S,先找到前一个节点p)
p->next = s;
删除结点: p->next = p->next->next;(先找到前一个节点p)
--------------------------------------------------------------
例:有一个带头结点的单链表L={a1,b1,a2,b2,a3,b3,...,an,bn},设计一个算法将其拆分成两个带头结点的单链表L1和L2,L1={a1,a2,a3...an},L2={bn,bn-1,...b1}.要求L1使用L的头结点
注:链表的插入分头插和尾插
例:有一个带头结点的单链表L,设计一个算法使其元素递增有序。
问:带头结点的单链表与不带头结点的单链表有何区别?
答:带头结点单链表可以在头节点中加入一些附加信息,并且有利于实现各种运算(删除和插入)。
//----------------------取第i个值-----------------------------------------
Status GetElem_L(LinkList &L,int i, ElemType &e) { // 算法2.8 // L为带头结点的单链表的头指针。 // 当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR LinkList p; p = L->next; int j = 1; // 初始化,p指向第一个结点,j为计数器 while (p && j<i) { // 顺指针向后查找,直到p指向第i个元素或p为空 p = p->next; ++j; } if ( !p || j>i ) return ERROR; // 第i个元素不存在 e = p->data; // 取第i个元素 return OK; } // GetElem_L
//----------------在带头结点的单链线性表L的第i个元素之前插入元素e--------
Status ListInsert_L(LinkList &L, int i, ElemType e) { // 算法2.9 // 在带头结点的单链线性表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
//---------在带头结点的单链线性表L中,删除第i个元素,并由e返回其值--------
Status ListDelete_L(LinkList &L, int i, ElemType &e) { // 算法2.10 // 在带头结点的单链线性表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
//------逆位序输入(随机产生)n个元素的值,建立带表头结点的单链线性表L-----
void CreateList_L(LinkList &L, int n) { // 算法2.11 // 逆位序输入(随机产生)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
//-----------------有序合并链表----------------------------------------
void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc) { // 算法2.12 // 已知单链线性表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
//--------------线性表的静态单链表存储结构------------------------
#define MAXSIZE 100 typedef struct { ElemType data; //数据域 int cur; //游标域,指示下一个元素在数组中的位置 }component,StaticList[MaxSize];
静态链表是借助一维数组来描述链表。数组中的一个分量表示一个结点,同时使用游标(cur)代替指针以指示结点在数组中的相对位置(游标为-1时表示相对应的结点为空).数组中的0分量可以看成头结点,其指针域指示静态链表的第一个结点,并将最后一个元素的指针域0构成循环结构
这种存储结构需预先分配一个较大空间,但是在进行线性表插入和删除操作时不需移动元素,仅需要修改“指针”,因此仍然具有链式存储结构的主要优点。
对于静态链表的初始化,一定要将其它没有元素的结点的.next设为-1,并将下标为[0].next设为0
对于静态链表可视为一个带头节点的循环链表,_StaticList[0]为其头结点,对于插入操作一般都先查找到前一个结点(前插),另对于新的插入项一定要存在下.next为-1的位置上。
另对于删除时要考虑链表是否为空表,对于插入要考虑是否表满
同样静态也有不带头节点的,类似于不带头节点的循环链表
同样可以构造类似于循环双链表的静态链表
等等总之灵活多样但一般不存在单链表式的静态链表
双链表
双链表的创建与单链表相似,只不过每个结点多了个PRIOR指针域
typedef struct DLNode{ ElemType data; struct DLNode *prior; struct DLNode *next; }DLNode, *DuLinkList;
双链表亦可分头插和尾插
特点(对称性):
p->next->prior = p;
p->prior->next = p;
//-------------在带头结点的双链循环线性表L的第i个元素之前插入元素e--------------
Status ListInsert_DuL(DuLinkList &L, int i, ElemType e) { //算法2.18 // 在带头结点的双链循环线性表L的第i个元素之前插入元素e, // i的合法值为1≤i≤表长+1。 DuLinkList p,s; if (!(p = GetElemP_DuL(L, i))) // 在L中确定第i个元素的位置指针p return ERROR; // p=NULL, 即第i个元素不存在 if (!(s = (DuLinkList)malloc(sizeof(DuLNode)))) return ERROR; s->data = e; s->prior = p->prior; p->prior->next = s; s->next = p; p->prior = s; return OK; } // ListInsert_DuL
//--------------删除带头结点的双链循环线性表L的第i个元素-----------------
Status ListDelete_DuL(DuLinkList &L, int i, ElemType &e) {//算法2.19 // 删除带头结点的双链循环线性表L的第i个元素,i的合法值为1≤i≤表长 DuLinkList p; if (!(p = GetElemP_DuL(L, i))) // 在L中确定第i个元素的位置指针p return ERROR; // p=NULL, 即第i个元素不存在 e = p->data; p->prior->next = p->next; p->next->prior = p->prior; free(p); return OK; } // ListDelete_DuL
循环链表
分为带头结点的循环单链表和循环双链表。
其判断结尾条件是:p->next == L(头结点)
因此头结点也连在整个循环链表中
针对于其初始化:
L->prior = L;
L->next = L;
一般为解决特殊问题还存在不带头结点的循环单链表和循环双链表(如约瑟夫环)
--------------------------------------------------------------------------------
一元多项式的表示及相加
//-------输入m项的系数和指数,建立表示一元多项式的有序链表P------------
void CreatPolyn(PLinkList &P, int m) { // 算法2.22 // 输入m项的系数和指数,建立表示一元多项式的有序链表P PLink h, q, s; PElemType e; int i; InitList(P); h = GetHead(P); e.coef = 0.0; e.expn = -1; SetCurElem(h, e); // 设置头结点 for (i=1; i<=m; ++i) { // 依次输入m个非零项 // scanf ("%f,%d\n",&e.coef, &e.expn); e.coef = (float)(random(1, 90) + random(10)/10.0); if (random(2)) e.coef = -e.coef; e.expn=e.expn+random(1,10); //产生随机的数据,但是expn值是递增的 if (!LocateElem(P, e, q, cmp)) { // 当前链表中不存在该指数项 if (MakeNode(s,e)) InsFirst(q, s); // 生成结点并插入链表 if(q==P.tail) P.tail=s; } else i--; // 如果没有产生插入,则将i值减1 } } // CreatPolyn
//---------打印输出一元多项式---------------
Status PrintfPoly(PLinkList P) { int i=0; PLink q=P.head->next; while (q) { if (fabs(q->data.coef) > 0.005) { if (i>0) { if (q->data.coef>0.005) printf(" + "); else printf(" - "); printf("%.2f", fabs(q->data.coef)); } else printf("%.2f", q->data.coef); if (q->data.expn>=1) printf("x"); if (q->data.expn>1) printf("^%d", q->data.expn); } q=q->next; if (++i % 6 == 0) printf("\n "); } printf("\n"); return OK; }
//-------------多项式加法-----------------
void AddPolyn(PLinkList &Pa, PLinkList &Pb) { // 算法2.23 // 多项式加法:Pa = Pa+Pb,利用两个多项式的结点构成"和多项式"。 PLink ha,hb,qa,qb; PElemType a, b, temp; float sum; ha = GetHead(Pa); // ha和hb分别指向Pa和Pb的头结点 hb = GetHead(Pb); qa = NextPos(Pa,ha); // qa和qb分别指向La和Lb中当前结点 qb = NextPos(Pb,hb); while (qa && qb) { // Pa和Pb均非空 a = GetCurElem (qa); // a和b为两表中当前比较元素 b = GetCurElem (qb); switch (Compare(a,b)) { case -1: // 多项式PA中当前结点的指数值小 ha = qa; qa = NextPos (Pa, qa); break; case 0: // 两者的指数值相等 sum = a.coef + b.coef ; if (sum != 0.0) { // 修改多项式PA中当前结点的系数值 temp.coef=sum; temp.expn=a.expn; SetCurElem(qa, temp) ; ha = qa; } else { // 删除多项式PA中当前结点 DelFirst(ha, qa); FreeNode(qa); } DelFirst(hb, qb); FreeNode(qb); qb = NextPos(Pb, hb); qa = NextPos(Pa, ha); break; case 1: // 多项式PB中当前结点的指数值小 DelFirst(hb, qb); InsFirst(ha, qb); qb = NextPos(Pb, hb); ha = NextPos(Pa, ha); break; } // switch } // while if (!Empty(Pb)) Append(Pa, qb); // 链接Pb中剩余结点 FreeNode(hb); // 释放Pb的头结点 } // AddPolyn
数据结构实验——线性表,链表
----------------------------
http://pan.baidu.com/share/link?shareid=2216128142&uk=186843953
源文件,实验报告分享。