浅析数据结构-线性结构之线性表
线性结构的特点:
- ① 存在一个唯一的被称为“第一个”的数据元素;
- ② 存在一个唯一的被称为“最后一个”的数据元素;
- ③ 除第一个元素外,每个元素均有唯一一个直接前驱;
- ④ 除最后一个元素外,每个元素均有唯一一个直接后继。
线性表:是由n(n≧0)个数据元素(结点)a1,a2, …an组成的有限序列。该序列中的所有结点具有相同的数据类型。其中数据元素的个数n称为线性表的长度。
当n=0时,称为空表。
当n>0时,将非空的线性表记作: (a1,a2,…an)
a1称为线性表的第一个(首)结点,an称为线性表的最后一个(尾)结点。
a1,a2,…ai-1都是ai(2≦i≦n)的前驱,其中ai-1是ai的直接前驱;
ai+1,ai+2,…an都是ai(1≦i ≦n-1)的后继,其中ai+1是ai的直接后继。
注:一个数据元素可以由若干个数据项组成。在这情况下,常把数据元素称为记录,含有大量记录的线性表称为文件。
线性表的表示与实现
顺序存储 :[数组]把线性表的结点按逻辑顺序依次存放在一组地址连续的存储单元里。用这种方法存储的线性表简称顺序表。
- ①什么是数组?
元素类型相同,大小相等。
- ②数组的优缺点:
- 优点:
1、按照索引查询元素速度快
2、能存储大量数据
3、按照索引遍历数组方便
- 缺点:
1、根据内容查找元素速度慢
2、数组的大小一经确定不能改变。
3、数组只能存储一种类型的数据
4、增加、删除元素效率慢
5、未封装任何方法,所有操作都需要用户自己定义。
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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
#include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #define MaxSize 100 #define ElemType int #define Status int using namespace std;
typedef struct { ElemType data[MaxSize]; int length; }SqList;
Status InitList(SqList &L) { memset(L.data, 0, sizeof(L)); L.length = 0; return 0; }
bool CreatList(SqList &L, int n) { if (n<0 || n>MaxSize)false; for (int i = 0; i<n; i++) { int temp; scanf("%d", &L.data[i]); L.length++; } return true; }
bool InsertList(SqList &L, int i, ElemType e) { if (i<1 || i>L.length + 1) { printf("位置无效!!!\n"); return false; } if (L.length >= MaxSize) { printf("当前存储空间已满!!!\n"); return false; } for (int j = L.length; j >= i; j--) { L.data[j] = L.data[j - 1]; } L.data[i - 1] = e; L.length++; return true; }
bool ListDelete(SqList &L, int i) { if (i<1 || i>L.length) { printf("位置无效!!!\n"); return false; } for (int j = i; j <= L.length - 1; j++) { L.data[j - 1] = L.data[j]; } L.length--; return true; }
int LocateElem(SqList L, ElemType e) { for (int i = 0; i<L.length; i++) { if (L.data[i] == e) return i + 1; } return 0; }
void Reverse(SqList &L) { if (L.length) for (int i = 0; i<L.length - 1 - i; i++) { int t = L.data[i]; L.data[i] = L.data[L.length - 1 - i]; L.data[L.length - 1 - i] = t; } }
void PrintList(SqList L) { printf("当前顺序表所有元素:"); for (int i = 0; i<L.length; i++) { printf("%d ", L.data[i]); } printf("\n"); }
void Create(SqList &L) { int n; bool flag; L.length = 0; printf("请输入要创建的顺序表长度(>1):"); scanf("%d", &n); printf("请输入%d个数(空格隔开):", n); flag = CreatList(L, n); if (flag) { printf("创建成功!\n"); PrintList(L); } else printf("输入长度非法!\n"); }
void Insert(SqList &L) { int place; ElemType e; bool flag; printf("请输入要插入的位置(从1开始)及元素:\n"); scanf("%d%d", &place, &e); flag = InsertList(L, place, e); if (flag) { printf("插入成功!!!\n"); PrintList(L); } }
void Delete(SqList &L) { int place; bool flag; printf("请输入要删除的位置(从1开始):\n"); scanf("%d", &place); flag = ListDelete(L, place); if (flag) { printf("删除成功!!!\n"); PrintList(L); } }
void Search(SqList L) { ElemType e; int flag; printf("请输入要查找的值:\n"); scanf("%d", &e); flag = LocateElem(L, e); if (flag) { printf("该元素位置为:%d\n", flag); } else printf("未找到该元素!\n"); }
void menu() { printf("********1.创建 2.插入*********\n"); printf("********3.删除 4.查找*********\n"); printf("********5.倒置 6.输出*********\n"); printf("********7.退出 *****************"); } int main() { SqList L; int choice; InitList(L); while (1) { menu(); printf("请输入菜单序号:\n"); scanf("%d", &choice); if (7 == choice) break; switch (choice) { case 1:Create(L); break; case 2:Insert(L); break; case 3:Delete(L); break; case 4:Search(L); break; case 5:Reverse(L); break; case 6:PrintList(L); break; default:printf("输入错误!!!\n"); } } return 0; }
|
g++和CLion,运行界面:
这种写法是所见的大多的写法,个人认为这种写法稍欠些想法,在结构体中为什么要直接放入一个数组呢?我们是不是可以这么写:
1 2 3 4 5 6
| typedef struct { ElemType *data; int length; int nLength; }SqList;
|
然后在在生成顺序表的时候,给ElemType *data赋值空间,类似于这么写:
1 2
| ElemType* data = (ElemType*)malloc(sizeof(ElemType) * nLength);
|
看过书的同学看到这就明白了,这种写法就是严奶奶书上的写法。笔者稍微手写试了一下。还有一些完善的地方,读者可自行修改。过于简单,注释就不写了。
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 84 85 86 87 88 89 90 91
| #include <iostream> #include <cstdlib> using namespace std; typedef int ElemType; typedef struct Node { ElemType *elem; int length; int cnt; }Sqlist,*SqlList; void Listinit(SqlList L,int length) { L->elem = (ElemType *)malloc(length * sizeof(ElemType)); if(NULL == L->elem) { cout << "动态内存分配失败!" << endl; exit(-1); } else { L->length = length; L->cnt = 0; } return; } bool is_Listempty(SqlList L) { return (0 == L->cnt); } bool is_Listfull(SqlList L) { return L->cnt == L->length; } bool ListInsert(SqlList L,int pos,int val) { if(is_Listfull(L)) return false; int i; if(pos < 1 || pos > L->cnt + 1) return false; for(i = L->cnt - 1;i >= pos - 1;--i) { L->elem[i + 1] = L->elem[i]; } L->elem[pos - 1] = val; ++L->cnt; return true; } bool ListDelete(SqlList L,int pos,int *pval) { if(is_Listempty(L)) return false; int i; if(pos < 1 || pos > L->cnt) return false; for(i = pos;i < L->cnt;++i) { L->elem[i + 1] = L->elem[i]; } --L->cnt; return true; } void Reverse(SqlList L) { if(L->cnt) { int i = 0; int j = L->cnt - 1; while(i < j) { L->elem[i] = L->elem[i] ^ L->elem[j]; L->elem[j] = L->elem[i] ^ L->elem[j]; L->elem[i] = L->elem[i] ^ L->elem[j]; ++i; --j; } return; } } void ListPrint(SqlList L) { if(is_Listempty(L)) cout << "数组为空!" << endl; else{ cout << "数组有效个数:" << L->cnt << endl; for(int i = 0;i < L->cnt;++i) cout << L->elem[i] << " "; cout << endl; } } int main(){ ElemType data; Sqlist L; Listinit(&L,10); ListInsert(&L,1,50); ListInsert(&L,2,60); ListInsert(&L,3,70); ListPrint(&L); ListDelete(&L,2,&data); Reverse(&L); ListPrint(&L); return 0; }
|
时间复杂度分析
时间主要耗费在数据元素的比较和移动操作上。
首先,在线性表L中查找值为x的结点是否存在;
其次,若值为x的结点存在,且在线性表L中的位置为i ,则在线性表L中删除第i个元素。
设在线性表L删除数据元素概率为Pi,不失一般性,设各个位置是等概率,则Pi=1/n。
◆ 比较的平均次数: Ecompare=∑pi * i (1≦i≦n)
∴ Ecompare=(n+1)/2 。
◆ 删除时平均移动次数:Edelete=∑pi * (n-i) (1≦i≦n)
∴ Edelete=(n-1)/2 。 平均时间复杂度:Ecompare+Edelete=n ,即为O(n)