数据结构与算法(二)--- 线性表之顺序存储结构

一、顺序存储结构

数据在存放的过程中,是有顺序的,内存(存放的引用)是连续的。实际开发中使用ArrayList。

在这里插入图片描述

顺序表的概念:

  • a1是a2的前驱,ai+1 是ai的后继,a1没有前驱,an没有后继
  • n(size)为线性表的长度 ,若n == 0 时,线性表为空表,若n==size时,线性表放满了。

【例1】

线性表:int[] array = new int[]{1, 2, 3, 4, 5}

【例2】

在这里插入图片描述

class Students {
    Student[40] stus; // 对象数组是线性表
    int size;
}

二、顺序表的操作

顺序表的操作:查询,修改,插入,删除,排序

(一)查询(二分查找请见后续文章)

public int search(int[] list, int des) {
    for (int i = 0; i < list.length; i++) {
        if (list[i] == des) {
            return i;
        }
    }
    return -1;
}

(二)修改

public void modify(int[] list, int index, int des) {
    list[index] = des;
}

(三)插入

不可以随意的插入,必须要把要插入的位置以后全部往后移,才能插入到指定的位置。

在这里插入图片描述

// 数组要预留出来空余内容,暂时不考虑动态数组
public void instert(int[] list, int index, int des) {
    // 如果在尾部插入数据,那么for循环没有执行
    for (int i = list.length - 1; i > index; i--) {
        list[i] = list[i-1];
    }
    list[index] = des;
}
  • 优点:尾部插入效率高,支持随机访问
  • 缺点:中间插入或者删除效率低

(四)删除

在这里插入图片描述

public void delete(int[] list, int index) {
    for (int i = index; i < list.length - 1; i++) {
        list[i] = list[i+1];
    }
    list[list.length - 1] = 0;
}

三、顺序表的操作之【排序】 – 蛮力法

蛮力法(brute force method,也称为穷举法或枚举法) 是一种简单直接地解决问题的方法, 常常直接基于问题的描述, 所以,蛮力法也是最容易应用的方法。 但是,用蛮力法设计的算法时间特性往往也是最低的(当n => ∞的时候), 典型的指数时间算法一般都是通过蛮力搜索而得到的 。(即输入资料的数量依线性成长,所花的时间将会以指数成长)

Arrays.sort(Object[] a); 实际上用的是binarySort + 递归 + 分开截断,至少执行了100行代码以上,所以不建议直接使用JDK提供的排序方法

在这里插入图片描述

  • 当n => ∞的时候,效率是最低的。【e.g.】y = n2 ,当n = 100时,y=10000,需要计算10000才能获得最终的排序。
  • 但是如果在数据量足够小的情况下,是所有算法中最快的。

(一)冒泡排序

通过相邻的两两比较后,将最值固定到当前排的最后面。

在这里插入图片描述
关键代码的执行次数,就是交换的次数:

  • 第一次:n次
  • 第二次:n-1次
  • 第三次:n-2次
  • 第m次:1次

总共交换次数(即时间复杂度):(n-1) + (n-2) + (n-3) + … + 1 = ((n-1) * ((n-1) + 1 ) / 2 = n * (n-1) / 2
当数据量足够小(3-5个数据),时间复杂度就趋向于n。如果n < 5,即会比任何排序执行速度都快。

public void bubbleSort(int[] list) {
    for (int i = list.length - 1; i > 0; i--) {
        boolean flag = true;
        for (int j = 0; j < i; j++) {
            if (list[j] > list[j + 1]) {
                int temp = list[j];
                list[j] = list[j + 1];
                list[j + 1] = temp;
                flag = false;
            }
        }
        if (flag) { // 减少一定的排序次数
            break;
        }
    }
}

【实例】斗牛采用的就是冒泡排序,具体见GitHub。

(二)选择排序

先把第一位固定下来,再从后面挑最小的,然后交换,最值在最前面
选择排序的交换次数会比冒泡排序少。

在这里插入图片描述

public void selectSort(int[] list) {
    for (int i = 0; i < list.length; i++) {
        int index = i;
        for (int j = index + 1; j < list.length; j++) {
            if (list[i] > list[j]) {
                index = j;  // 获取最值的角标
            }
        }
        if (i != index) { // 减少循环次数
            int temp = list[i];
            list[i] = list[index];
            list[index] = temp;
        }
    }
}

四、ArrayList原码简单分析

public class ArrayList<E> {
    private static final int DEFAULT_CAPACITY = 10; // 默认容量,会开辟一个大小为10的数组
    private static final Object[] EMPTY_ELEMENTDATA = {};  // 初始化一个长度为空的数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // (默认容量)初始化一个长度为空的数组
    transient Object[] elementData; // 对象数组(什么对象都能存储)
                                    // transient表示不参与序列化,瞬态变量,一瞬间的变量,不具备持久化功能,不会存到物理存储空间上
    private int size;
    
    // 默认构造,创建一个长度为空的数组
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    // 给定一个数组容量,创建对应长度的数组
    public ArrayList(int initialCapacity) { 
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
        }
    }    
    // 【增】添加到末尾
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 检查数组容量
        elementData[size++] = e;
        return true;
    }

   // 【增】在指定位置上添加对应的元素
    public void add(int index, E element) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        ensureCapacityInternal(size + 1);  // 检查数组容量
        System.arraycopy(elementData, index, elementData, index + 1,  size - index); 
                        // 将数组elementData的index位置,拷贝size - index个元素,到数组elementData的 index + 1的位置
                        // 即index位置上的元素往后挪动一位。
                        // arraycopy通过循环实现数组的拷贝
        elementData[index] = element;  // 将新的元素element,放到index的位置上
        size++;
    }
    
	// 容量检测的过程,如果当前size + 1,还满足数组容量,直接操作,如果不满足要求,扩容。
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity); -- > 调用到: grow(minCapacity);
    }

	// 数组扩容
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1); // oldCapacity + oldCapacity / 2^1 = 1.5 * oldCapacity
                                                            //(右移:除以2的几次幂, 左移:乘以2的几次幂)
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity); // 数组拷贝的动作,把原来的elementData拷贝的新的newCapacity
    }
}
  • 扩容的过程:
    在这里插入图片描述
  • 代码执行过程图示:
    System.arraycopy(elementData, index, elementData, index + 1, size - index);
    elementData[index] = element;
    在这里插入图片描述
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值