赞
踩
顺序存储
和链式存储
两种存储结构来表示。
顺序存储
的线性表称为顺序表,也称为静态线性表。链式存储
的线性表称为链表,也称为动态线性表。顺序表,就是顺序存储的线性表。
顺序存储是用一组地址连续的存储单元依次存放线性表中各个数据元素的存储结构。
在逻辑上,数据ABCD是连续
在物理上,地址也是连续的
可以使用数组
来描述数据结构中的顺序存储结构。
地址公式
- //第i的地址 = 第一个地址 + 第几个 * 存储单位
- Loc(ai) = Loc(a0) + i * c
在线性表中逻辑上相邻的数据元素,在物理存储位置上也是相邻的。
存储密度高。但需要预先分配“足够”的存储空间。
存储密度 = 数据元素存储空间 / 数据元素实际占用空间
在顺序表中,存储密度为1。
便于随机存储。(数组中可以通过下标进行存储)
不便于插入和删除操作。两种操作都会引起大量的数据移动。
需要:在顺序表第i个位置处插入一个新元素。
顺序表插入操作:将第i个数据元素及其之后的所有的数据元素,后移一个存储位置,再将新元素插入到i处。
前置:类中成员
- public class SqList {
- private Object[] listElem; //线性表的存储空间
- private int curLen; //线性表的当前长度
- }
插入操作算法
- /**
- * @Param i 第i个位置
- * @Param x 需要插入的数据
- */
- public void insert(int i, Object x) {
- //0.1 满校验:存放实际长度 和 数组长度 一样
- if(curLen == listEle.length) {
- throw new Exception("已满");
- }
- //0.2 非法校验,在已有的数据中间插入 [0, curLen],必须连续,中间不能空元素
- if(i < 0 || i > curLen) {
- throw new Exception("位置非法");
- }
- //1 将i及其之后后移
- for(int j = curLen ; j > i; j --) {
- listEle[j] = listEle[j-1];
- }
- //2 插入i处
- listEle[i] = x;
- //3 记录长度
- curLen ++;
- }
插入时间复杂度:O(n)
算法:
- public void remove(int i ) throws Exception {
- // 0.1 校验非法数据
- if(i < 0 || i > curLen - 1 ) {
- throw new Exception("位置非法");
- }
- // 1 将i之后向前移动
- for(int j = i ; j < curLen - 1 ; j ++ ) {
- listEle[j] = listEle[j+1];
- }
- // 2 长度减一
- curLen--;
- }
删除时间复杂度:O(n)
需求:查找指定数据的索引号
算法1:循环遍历已有数据,进行判断,如果有返回第一个索引号,如果没有返回-1
- public int indexOf(Object x) {
- for(int i = 0; i < curLen ; i ++) {
- if(listEle[i].equals(x)) {
- return i;
- }
- }
- return -1;
- }
算法2:使用变量记录没有匹配到索引
- public int indexOf(Object x) {
- int j = 0; //用于记录索引信息
- while(j < curLen && !listElem[j].equals(x)) {
- j++;
- }
- // j记录索引小于数量
- if(j < curLen ) {
- return j;
- } else {
- return -1
- }
- }
查询时间复杂度:O(n)
采用链式存储方式存储的线性表称为链表。
链表中每一个结点包含存放数据元素值的数据域和存放逻辑上相邻节点的指针域。
非空表:
空表:空表的指针域为空指针null
- public class Node{
- public Object data; //数据域
- public Node next; //指针域
- }
- public int length() {
- Node p = head.next; // 获得第一个结点
- int length = 0; // 定义一个变量记录长度
- while(p != null) {
- length ++; //计数
- p = p.next; //p指向下一个结点
- }
- return length;
- }
- public Object get(int i) {
- Node p = head.next; //获得头结点
- int j = 0; //已经处理的结点数
- while(p != null && j < i) { //链表没有下一个 && 处理有效部分
- p = p.next;
- j++;
- }
- if(i < 0 || p == null) {
- throw new Exception("元素不存在");
- }
- return p.data; //返回数据
- }
- //查询给定值的索引号,如果没有返回-1
- public int indexOf(Object x) {
- Node p = head.next; // 获得头结点
- int j = 0; // 不匹配元素的个数
- while(p != null && !p.data.equals(x)) { // 循环依次找【不匹配】元素
- p = p.next;
- j++;
- }
- // 返回匹配索引,如果没有返回-1
- if(p != null) {
- return j;
- } else {
- return -1;
- }
- }
- public void insert(int i , Object x) {
- Node p = ...; // 获得i前驱,结点p
- Node s = new Node(x); //创建新结点s
- s.next = p.next; //必须先执行
- p.next = s; //然后再执行
- }
需求:删除i结点
- public void remove(int i) {
- // 获得i的前驱结点p
- p.next = p.next.next;
- }
- // i最小值0,表示获得头结点head
- // 算法内部,使用-1表示头结点head,结点移动从头结点head开始。
- public void previous(int i) {
- Node p = head; //从头结点head开始移动
- int j = -1; //使用-1表示头结点的索引
- while(p != null && j < i-1 ) { // i-1前驱的下标
- p = p.next;
- j++;
- }
- if(p == null || i < 0) { // i-1 < j
- throw new RuntimeException("i不合法");
- }
- //p就是需要获得前驱
- }
循环链表也称为环形链表,其结构与单链表相似,只是将单链表的首尾相连。
- // p结点是尾结点
- p.next = head;
- /* 变量
- a尾: taila
- a头:taila.next
- b尾:tailb
- b头:tailb.next
- */
- // 先将b尾指向a的头,需要定义p记录b尾原来的值
- // 再将a的尾指向b的头
- Node p = tailb.next; //记录b尾的指向,也就是b头
- tailb.next = taila.next; //b尾指向a头
- taila.next = p.next; //a尾执行b头
- /* 变量
- 结点a:p.prior
- */
- p.prior.next = s; //结点a指向结点s
- s.prior = p.prior; // 结点s指向结点a
-
- p.prior = s; //结点p指向结点s
- s.next = p; //结点s执行结点p
- //注意:必须先处理结点a,否则无法获得结点a
- // a.next = b
- p.prior.next = p.next;
- // b.prior = a
- p.next.prior = p.prior
=========================================================
好啦,以上就是本期全部内容,能看到这里的人呀,都是能人。
十年修得同船渡,大家一起点关注。
我是♚焕蓝·未来,感谢各位【能人】的:点赞、收藏和评论,我们下期见!
各位能人们的支持就是♚焕蓝·未来前进的巨大动力~
注:如果本篇Blog有任何错误和建议,欢迎能人们留言!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。