Array数组
- 概念
数组简单来说就是将所有的数据排成一排放在系统分配的内存块上,通过使用特定元素的索引作为数组的下标,可以在常数范围内访问数组元素的结构。
- 数组的优缺点
- 优点
- 简单且易用;
- 访问元素快;
- 缺点
- 大小固定:数组的大小是静态的(在使用前必须制定数组的大小);
- 分配一个连续空间块:数组初始分配空间时,有时候无法分配能存储整个数组的内存空间(当数组规模太大时);
- 基于位置的插入操作实现复杂:如果要在数组中的给定位置插入元素,那么可能就会需要移动存储在数组中的其他元素,这样才能腾出指定的位置来放插入的新元素;而如果在数组的开始位置插入元素,那么这样的移动操作开销就会很大。
- 优点
- 数据结构分析
- 创建自定义数组
public class Array<E>{
private E[] data;
private int size;
/* 一些成员方法 */
}
说明:泛型封装数组、成员变量data用来存储数据、成员变量size有效个数,在构造函数里设置size从0开始。
-
- 构造函数
// 构造函数,传入数组的容量capacity构造Array
public Array(int capacity) {
data = (E[]) new Object[capacity];
size = 0;
}
// 无参数的构造函数,默认数组的容量capacity=10
public Array() {
this(10);
}
说明:capacity是数组的容量,
-
- 成员方法
// 获取数组的容量
public int getCapacity() {
return data.length;
}
// 获取数组中的元素个数
public int getSize() {
return size;
}
// 返回数组是否为空
public boolean isEmpty() {
return size == 0;
}
// 在index索引的位置插入一个新元素e
public void add(int index, E e) {
if (index < 0 || index > size)
throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");
if (size == data.length)
resize(2 * data.length);
for (int i = size - 1; i >= index; i--)
data[i + 1] = data[i];
data[index] = e;
size++;
}
// 向所有元素后添加一个新元素
public void addLast(E e) {
add(size, e);
}
// 在所有元素前添加一个新元素
public void addFirst(E e) {
add(0, e);
}
// 获取index索引位置的元素
public E get(int index) {
if (index < 0 || index >= size)
throw new IllegalArgumentException("Get failed. Index is illegal.");
return data[index];}
// 修改index索引位置的元素为e
public void set(int index, E e) {
if (index < 0 || index >= size)
throw new IllegalArgumentException("Set failed. Index is illegal.");
data[index] = e;
}
// 查找数组中是否有元素e
public boolean contains(E e) {
for (int i = 0; i < size; i++) {
if (data[i].equals(e))
return true;
} return false;
}
// 查找数组中元素e所在的索引,如果不存在元素e,则返回-1
public int find(E e) {
for (int i = 0; i < size; i++) {
if (data[i].equals(e))
return i;
} return -1;
}
// 从数组中删除index位置的元素, 返回删除的元素
public E remove(int index) {
if (index < 0 || index >= size)
throw new IllegalArgumentException("Remove failed. Index is illegal.");
E ret = data[index];
for (int i = index + 1; i < size; i++)
data[i - 1] = data[i]; size--;
data[size] = null;
// loitering objects != memory leak
if (size == data.length / 4 && data.length / 2 != 0)
resize(data.length / 2);
return ret;
}
// 从数组中删除第一个元素, 返回删除的元素
public E removeFirst() {
return remove(0);
}
// 从数组中删除最后一个元素, 返回删除的元素
public E removeLast() {
return remove(size - 1);
}
// 从数组中删除元素e
public void removeElement(E e) {
int index = find(e);
if (index != -1)
remove(index);
}
@Overridepublic String toString() {
StringBuilder res = new StringBuilder();
res.append(String.format("Array: size = %d , capacity = %d\n", size, data.length));
res.append('[');
for (int i = 0; i < size; i++) {
res.append(data[i]);
if (i != size - 1)
res.append(", ");
} res.append(']');
return res.toString();}
// 将数组空间的容量变成newCapacity大小
private void resize(int newCapacity) {
E[] newData = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++)
newData[i] = data[i];
data = newData;
}
声明:
- 向数组末添加元素:
- 向数组指定位置添加元素:
- 数组指定位置删除元素:
- 时间复杂度分析
- 添加操作
分析得出addLast()方法是与n无关的,属于O(1)复杂度;addFirst()和add()方法涉及到需要移动数组元素,所以都是O(n)复杂度,综合起来添加操作的复杂度就是O(n)。
-
- 删除操作
分析得出removeLast()方法是与n无关的,所以为O(1)复杂度;而removeFirst()和remove()方法都涉及到挪动数组元素,所以都是O(n)复杂度,包括resize()方法;综合起来删除操作的复杂度就是O(n)。
-
- 修改操作
修改操作知道了修改元素索引,时间复杂度就是O(1)的。
-
- 查询操作
在查询操作中,如果我们已知索引,那么复杂度为O(1);如果未知索引,我们需要遍历整个数组,那么复杂度为O(n)级别。
- 总结
增加:O(n);
删除:O(n);
修改:已知索引 O(1);未知索引 O(n);
查询:已知索引 O(1);未知索引 O(n);
- Leecode题目
- 买卖股票的最佳时机
- 题目:
- 买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
-
-
- 例子:
-
-
-
- 解题:
-
public int maxProfit(int[] prices) {
int minValue = Integer.MAX_VALUE;
int maxProfit = 0;
for(int i=0;i<prices.length;i++){
if(prices[i] < minValue){
minValue = prices[i];
}
if(prices[i] - minValue > maxProfit){
maxProfit = prices[i] - minValue;
}
}
return maxProfit;
}
-
- 买卖股票的最佳时机∥
- 题目:
- 买卖股票的最佳时机∥
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
-
-
- 例子:
-
-
-
- 解题:贪心算法
-
public int maxProfit(int[] prices) {
int ans = 0;
int n =prices.length;
for(int i = 1;i<n;i++){
ans += Math.max(0,prices[i] - prices[i-1]);
}
return ans;
}
-
- 删除排序数组中的重复项
- 题目:
- 删除排序数组中的重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
-
-
- 例子:
-
-
-
- 解题:
-
public int removeDuplicates(int[] nums) {
int slow = 0;
for(int fast=0;fast<nums.length;fast++){
if(nums[slow] != nums[fast]){
slow++;
nums[slow] = nums[fast];
}
}
return slow+1;
}