数据结构&算法05-线性表之数组
Posted 李柱明
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构&算法05-线性表之数组相关的知识,希望对你有一定的参考价值。
前言
本笔记主要记录数组的一些基础特性及操作。
顺便解答下为什么大部分编程语言的的数组是从 0 开始的。
李柱明博客:https://www.cnblogs.com/lizhuming/p/15487306.html
线性结构与非线性结构
线性结构:
- 线性表就是数据排成像一条线一样的结构。每个线性表上的数据最多只有前和后两个方向。
- 包括:数组、链表、队列和栈等。
非线性结构:
- 在非线性表,数据之间不是简单的前后关系,通过下图对比线性结构和非线性结构
- 包括:树、图等。
参考图:
数组
数组的两个限制
数组有两个限制:
- 数组为线性结构。
- 是连续的内存空间和相同类型的数据。
数组的随机访问特性
基于以上两个限制,使得数组具有随机访问的特性。
小笔记:
-
计算机通过地址来访问内存中的数据。
-
寻址通过以下公式寻址:
a[i]_address = base_address + i * data_type_size;
而随机访问是只要知道数组对应的下标,我们就可以直接通过寻址公式知道该数据,不需要查找什么的。使得时间复杂度为 O(1)。
小笔记:
-
数组和链表的区别:
- 数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)。
- 链表适合增删,时间复杂度为 O(1)。
数组的操作
数组的操作主要是增删。
插入操作
插入数据,若不是尾部且不能留空,则需要搬移部分数据。
分析:
-
若插入尾部,则不需要搬移数据,插入操作的时间复杂度为 O(1)。
-
若插入第 k 个位置中。其插入操作的时间复杂度为 O(n)。
- 特定情况下,可以把原有第 k 个位置中的数据挪到最后。这样其插入操作的时间复杂度为 O(1)。
删除操作
删除数据,若不是尾部且不能留空,则需要搬移部分数据。
分析:
-
若删除尾部,则不需要搬移数据,删除操作的时间复杂度为 O(1)。
-
若删除第 k 个位置中。其删除操作的时间复杂度为 O(n)。
- 特定情况下,若删除的位前面几个,则可以直接修改数组的起始位置即可。
数组越界
小笔记:一个由 C/C++ 编译的程序占用的内存分为以下几个部分:
-
堆区(stack):
- 生长开口方向向上。(底为低地址、小端)
- 一般由程序员分配释放, 若程序员不释放,程序结束时可能
由 OS 回收 。
-
栈区(heap):
- 生长开口方向向下。(底为低地址、小端)
- 由编译器自动分配释放 ,存放函数的参数值,局部变量的值
等。其操作方式类似于数据结构中的栈。
-
全局区(静态区)(static):
- 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
- 程序结束后有系统释放
-
文字常量区:
- 常量字符串就是放在这里的。
- 程序结束后由系统释放。
-
程序代码区:
- 存放函数体的二进制代码。
参考代码:
int main(int argc, char* argv[])
{
int i = 0;
int arr[3] = {0};
for(; i<=3; i++)
{
arr[i] = 0;
printf("hello world\\n");
}
return 0;
}
分析:
- 该代码无限打印 hello world\\n 。就是因为内存溢出。
- 整型变量 i 和整型数组 arr[3] 存储在栈空间中。
- arr[3] 的存储位置就是 i 的存储位置。
- for 循环的合法循环 i 最大为 3,刚好在 i=3 的那次循环把 i 重置为 0 了。
这种数组越界的错误容易导致逻辑混乱。程序跑飞。
容器
针对数组类型,很多语言都提供了容器类,比如 Java 中的 ArrayList、C++ STL 中的 vector。
容器把很多数组操作的细节封装起来,如插入、删除等,且支持扩容。
数组下标
为什么大多数编程语言中,数组要从 0 开始编号,而不是从 1 开始呢?
- 寻址效率原因。
- 历史原因。
寻址:
-
正常寻址:
a[k]_address = base_address + k * type_size;
-
若数组下标起始为 1 的寻址:
a[k]_address = base_address + (k-1)*type_size;
每次寻址多了一个减法指令。
历史:
- C 语言设计者用 0 开始计数数组下标,之后的 Java、javascript 等高级语言都效仿了 C 语言。
小笔记:
- python 支持负下标。
线性表之顺序表
线性表的结构体定义:
#define MaxSize 100 typedef struct { int data[MaxSize]; int length; }Sqlist;
顺序表在内存中以数组形式保存,是一组连续的内存空间。
顺序表基本算法:
构造一个空的线性表:
//初始化线性表 STATUS InitList(Sqlist &L) { //置表长为0 L.length = 0; return OK; }
返回指定元素位置:
//定义返回线性表中某个元素的位置i int LocateElem(Sqlist L, int x) { int i; for (i = 0; i < L.length; i++) { if (x < L.data[i]) //返回元素位置 return i; } //没有元素比x大,则应该是第一个位置 return i; }
创建一个线性表:
//创建线性表 STATUS CreateList(Sqlist &L, int data[], int size) { if (size > MaxSize) return ERROR; int i = 0; for (i = 0; i < size; i++) { L.data[i] = data[i]; } L.length = size; return OK; }
插入一个元素(假设线性表中元素已升序排序)
//插入x后移动相应的元素位置,假设插入总能成功 STATUS InsertElem(Sqlist &L, int x) { int positionOfX, i; int sum = L.length; positionOfX = LocateElem(L, x); for (i = L.length - 1; i >= positionOfX; i--) { L.data[i+1] = L.data[i]; } L.data[positionOfX] = x; L.length++; //判断是否插入成功 if (L.length > sum) { return OK; } else return ERROR; }
删除指定位置的元素:
//删除指定位置的元素,并将删除元素返回 int DeleteElemAtPos(Sqlist &L, int p) { if (p<0 || p>L.length-1) return ERROR; int result = L.data[p]; int i; //注意是i<L.length-1,而不是<L.length for (i = p; i < L.length-1; i++) { L.data[i] = L.data[i + 1]; } --L.length; return result; }
在指定位置插入元素:
//在指定位置p插入元素 STATUS InsertElemAtPos(Sqlist &L, int p, int x) { if (p<0 || p>L.length || L.length == MaxSize) return ERROR; int i = 0; for (i = L.length - 1; i >= p; i--) { L.data[i + 1] = L.data[i]; } L.data[p] = x; L.length++; return OK; }
给线性表元素升序排序(可使用多种排序算法,本文使用冒泡):
//给线性表排序 STATUS SortList(Sqlist &L) { int i, j; int temp = 0; for(i=L.length-1;i>=1;i--) for (j = 0; j <= i-1; j++) { if (L.data[j] > L.data[j + 1]) { temp = L.data[j]; L.data[j] = L.data[j + 1]; L.data[j + 1] = temp; } } return OK; }
测试代码:
int main() { //创建一个新表 Sqlist *A; A = (Sqlist*)malloc(sizeof(Sqlist)); cout << "Initial Sqlist" << endl; InitList(*A); cout << "A的长度 " << A->length << endl << endl; int data[5] = { 1,3,2,3,5 }; CreateList(*A, data, 5); cout << "length of A is " << A->length << endl; cout << "初始化后的线性表A是:" << endl; PrintList(*A); cout << endl; cout << "给线性表排序之后是:" << endl; SortList(*A); PrintList(*A); cout << endl; cout << "插入一个元素后的列表" << endl; InsertElem(*A, 2); PrintList(*A); cout << "length of A is :" << A->length << endl << endl; cout << "插入元素0" << endl; InsertElem(*A, 0); PrintList(*A); cout << "length of A is " << A->length << endl << endl; cout << "after Delete elem 3 " << endl ; DeleteElemAtPos(*A, LocateElem(*A, 3)); PrintList(*A); cout << "length of A is " << A->length << endl; //解决显示窗口一显而过 system("pause"); return 0; }
以上是关于数据结构&算法05-线性表之数组的主要内容,如果未能解决你的问题,请参考以下文章