1.算法概述
算法:在计算机领域里,算法是一系列程序指令,用于处理特定的运算和逻辑问题。衡量算法优劣的主要标准是时间复杂度和空间复杂度。
数据结构:数据的组织、管理和存储格式,其使用的目的是为了高效的访问和修改数据。数据结构包含数组、链表这样的线性数据结构,也包含树、图这样的复杂数据结构。
时间复杂度:对一个算法运行时间长短的量度,用O表示,记作T(n)=O(f(n))
空间复杂度:对一个算法在运行过程中临时占用存储空间大小的量度用O表示,记作S(n)=O(f(n))。其中递归算法的空间复杂度和递归深度成正比。
2. 数组
2.1 数组的概念
数组对应的英文是array,是有限个相同类型的变量所组成的有序集合,数组中的每一个变量被称为元素。数组是最为简单、最为常用的数据结构。
数组的另一个特点,是在内存中顺序存储,因此可以很好地实现逻辑上的顺序表。
内存是由一个个连续的内存单元组成,每一个内存单元都有自己的地址。在这些内存单元中,有些被其他数据占用了,有些是空闲的。
如图,橙色格子代表空闲的存储单元,灰色格子代表已占用的存储单元,红色的连续格子代表数组在内存中的位置。
2.2 数组的基本操作
1,读取元素
由于数组在内存中顺序存储,所以只要给出一个数组下标,就可以读取到对应的数组元素。
int[] array = new int[]{3,1,2,5,4,7,5,1,9};
//输出下标为3的元素
System.out.println(array[3]);
这种根据下标读取元素的方式叫随机读取。
2,更新元素
把数组中某一个元素的值替换为一个新值。
int[] array = new int[]{3,1,2,5,4,7,5,1,9};
//给数组下标为3的元素赋值
array[3] = 10;
3,插入元素
存在三种情况:
- 尾部插入:直接把插入的元素放在数组尾部的空闲位置上
- 中间插入:先把插入位置及后面的元素向后移动,空出地方,再把要插入的元素放到对应的数组位置上
- 超范围插入:一个长度固定的数组,已经装满了元素,这是还想插入一个新元素。这涉及到数组的扩容。扩容的方法:创建一个新数组,长度是旧数组的2倍,再把旧数组中的元素复制过来,这样就实现了数组的扩容。
中间插入操作的完整实现代码:
/**
* 中间插入操作
*/
public class MyArray {
private int[] array;
private int size;
public MyArray(int capacity){
this.array = new int[capacity];
size = 0;
}
/**
* 数组插入元素
* @param index 插入的位置
* @param element 插入的元素
*/
public void insert(int index,int element) throws Exception{
//判断访问下标是否超出范围
if (index<0 || index>size){
throw new IndexOutOfBoundsException("超出数组实际元素范围!");
}
//从右向左循环,将元素逐个向右挪一位
for (int i = size-1; i >= index ; i--) {
array[i+1] = array[i];
}
//腾出的位置放入新元素
array[index] = element;
size++;
}
/**
* 输出数组
*/
public void output(){
for (int i = 0; i < size; i++) {
System.out.println(array[i]);
}
}
public static void main(String[] args) throws Exception{
MyArray myArray = new MyArray(10);
myArray.insert(0,3);
myArray.insert(1,7);
myArray.insert(2,9);
myArray.insert(3,5);
myArray.insert(1,6);
myArray.output();
}
}
运行结果:
超范围插入操作的完整实现代码:
/**
*超范围插入
*/
public class MyArray {
private int[] array;
private int size;
public MyArray(int capacity){
this.array = new int[capacity];
size = 0;
}
/**
* 数组插入元素
* @param index 插入的位置
* @param element 插入的元素
*/
public void insert(int index,int element) throws Exception{
//判断访问下标是否超出范围
if (index<0 || index>size){
throw new IndexOutOfBoundsException("超出数组实际元素范围!");
}
//如果实际元素达到数组容量上限,对数组进行扩容
if (size>=array.length){
resize();
}
//从右向左循环,将元素逐个向右挪一位
for (int i = size-1; i >= index ; i--) {
array[i+1] = array[i];
}
//腾出的位置放入新元素
array[index] = element;
size++;
}
/**
* 数组扩容
*/
public void resize(){
int[] arrayNew = new int[array.length*2];
//从旧数组复制到新数组
System.arraycopy(array,0,arrayNew,0,array.length);
array = arrayNew;
}
/**
* 输出数组
*/
public void output(){
for (int i = 0; i < size; i++) {
System.out.print(array[i]);
System.out.print("\t");
}
}
public static void main(String[] args) throws Exception{
MyArray myArray = new MyArray(4);
myArray.insert(0,3);
myArray.insert(1,7);
myArray.insert(2,9);
myArray.insert(3,5);
myArray.insert(1,6);
myArray.output();
}
}
输出结果:
4,删除元素
数组的删除操作和插入操作的过程相反,如果删除的元素位于数组中间,其后的元素都需要向前挪动1位。
删除操作的代码:
/**
* 数组删除元素
* @param index 删除的位置
*/
public int delete(int index) throws Exception{
if (index<0 || index>size){
throw new IndexOutOfBoundsException("超出数组实际元素范围");
}
int deleteElement = array[index];
for (int i = index; i < size-1 ; i++) {
array[i] = array[i+1];
}
size--;
return deleteElement; //返回被删除的元素
}
数组的优势和劣势
优势:拥有非常高效的随机访问能力,只要给出下标,就可以用常量时间找到对应的数组元素。二分查找就是利用数组的这个优势。
劣势:主要体现在插入和删除元素方面。由于数组元素连续紧密地存储在程序中,插入、删除元素都会导致大量元素被迫移动。
总的来说,数组所适合的是读操作多,写操作少的场景。