1 數組基礎知識回顧
數組的定義
數組是由n的相同類型的元素構成的有序序列,每個數據元素稱爲一個數組元素,每個元素受n個線性元素的約束,每個元素在n個線性關係中的序號稱爲該元素的下標,並稱該數組爲n維數組。
數組的存儲
大多數編程語言都支持數組數據類型進行存儲,一個數組的所有元素在內存中佔有一段連續的存儲空間。
- 以一維數組爲例:LOC(ai) = LOC(a0)+(i)*L;L是每個數組元素所佔的存儲單元;
- 以二維數組來說,有按列存儲和按行存儲的方式。以按行存儲爲例,先行後列,先存儲行號較小的元素,行號相等先存儲列號較小的元素。設二維數組行下標與列下表的範圍分別爲分別爲[l1,h1],[l2,h2]則存儲結構關係式爲LOC(ai,j)=LOC(al1,l2)+[(i-l1)*(h2-l2+1)+(j-l2)]*L;如下圖爲按行優先存儲的存儲方式。
- 多維數組可以依照二維數組來推,原理相同
數組的分類
數組分爲靜態數組與動態數組;
靜態數組:主要特點是在在程序編譯時分配空間的數組
動態數組:在程序運行時是會自動擴容。
Java實現數組結構
動手實現靜態數組默認存放10個元素,實現CRUD
import java.util.Objects;
<E>爲抽象數據類型實現泛型
public class ArrayList<E> {
private E [] data;//存放數組元素
private int size;//獲取數組的長度
//初始化數組
public ArrayList (int capacity){
data = (E[]) new Objects[capacity];//capacity爲數組容量
size = 0;//初始化
}
//默認容量爲10;
public ArrayList(){
this(10);
}
//獲取數組的容量
public int getCapacity(){
return data.length;
}
//添加元素
// 在index索引的位置插入一個新元素e
public void add(int index, E e){
if(size == data.length)
throw new IllegalArgumentException("Add failed. Array is full.");
if(index < 0 || index > size)
throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");
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);
}
//重寫Java的toString
@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append(String.format("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();
}
//按值查找,返回下標
public int find(E e){
for (int i = 0; i < size; i++) {
if(data[i]==e){
return i;
}
}
return -1;
}
//按照下標查找
public E findElement(int index){
if(index <0 || index > size){
throw new IllegalArgumentException("index range is invaild , Please reconsider");
}
return data[index];
}
//修改元素
public void setElement(int index,E element){
data[index] = element;
}
//刪除元素
public E remove(int index){
if(index <0 || index > size){
throw new IllegalArgumentException("remove range is invaild , Please reconsider");
}
E ret = data[index];
for (int i = index+1; i < size; i++) {
data[i-1]= data[i];
}
size--;
return ret;
}
}
測試
ArrayList arrayList = new ArrayList();
arrayList.addLast(10);
for (int i = 0; i <9 ; i++) {
arrayList.addLast(i);
}
System.out.println(arrayList);
int find = arrayList.find(3);
int finde = (int) arrayList.get(3);
System.out.println(String.format("按值查找:%d,按下標查找:%d",find,finde));
arrayList.set(3,5);
System.out.println(arrayList);
arrayList.remove(1);
System.out.println(arrayList);
結果
Array: size = 10 , capacity = 10
[10, 0, 1, 2, 3, 4, 5, 6, 7, 8]
按值查找:4,按下標查找:2
Array: size = 10 , capacity = 10
[10, 0, 1, 5, 3, 4, 5, 6, 7, 8]
Array: size = 9 , capacity = 10
[10, 1, 5, 3, 4, 5, 6, 7, 8]
實現動態數組
思路:在添加元素和刪除元素當中增加一個resize方法,可以修改size,
- resize()將舊的數組元素添加到新數組當中
- 進行擴容;每次可以擴容兩倍int size裏面設置擴容大小
if(size == data.length)
resize(2 * data.length);
resize方法
private void resize(int resize) {
E[] newData = (E[]) new Objects [resize];
for (int i = 0; i < data.length; i++) {
newData[i] = data[i];
}
data = newData;
}
測試
//默認大小爲10,如果添加20個元素會自動擴容兩次
for (int i = 0; i < 20; i++) {
arrayList.lastAdd(i);
}
System.out.println(arrayList);
//Capacity變成40實現兩次擴容
Size = 29,Capacity = 40
[10,1,2,3,4,5,6,7,8,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]//進行兩次擴容
練習
1.用動態數組解決數據存放的問題
編寫一段代碼,要求輸入一個整數N,用動態數組A來存放2~N之間所有5或7的倍數,輸出該數組。
示例:
輸入:
N = 100
輸出:
5 7 10 14 15 20 21 25 28 30 35 40 42 45 49 50 55 56 60 63 65 70 75 77 80 84 85 90 91 95 98 100
思路:直接遍歷從2~n的整數,取出其中能被5和7整除的整數
public void fiveOrSevenMultiple(int n){
for (int i = 2; i < n+1; i++) {
if(i%5 == 0 || i%7 == 0){
lastAdd(i);
}
}
}
arrayList.fiveOrSevenMultiple(100);
System.out.println(arrayList);
結果:
Size = 32,Capacity = 40
[5,7,10,14,15,20,21,25,28,30,35,40,42,45,49,50,55,56,60,63,65,70,75,77,80,84,85,90,91,95,98,100]
2.託普利斯矩陣問題
託普利斯矩陣問題就是沿着矩陣的反對角線對稱的矩陣,用Java判斷一個矩陣是否爲託普利斯矩陣。
實現思路,1.先遍歷矩陣行列的長度,2.判斷矩陣的[i][j]與[i+1][j+1]是否相等
public boolean topliciMatix(int [] [] matrix){
for (int i = 0; i < matrix.length-1; i++) {
for (int j =0;j<matrix.length[0];j++){
if(matrix[i][j] == matrix[i+1][j+1]){
return true;
}
}
}
return false;
}
int [][]commonmartix={{1,2,3},{2,3,4},{5,6,7}};
System.out.println(arrayList.topliciMatix(commonmartix));
int [][]toplicimartix = {{1,2,3},{5,1,2},{9,5,1}};
System.out.println(arrayList.topliciMatix(toplicimartix));
//結果
false
true
3.三數之和
給定一個包含n個整數的數組nums,判斷nums中是否存在三個元素a,b,c,使得a+b+c =0?找出所有滿足條件且不重複的三元組。
例如, 給定數組 nums = [-1, 0, 1, 2, -1, -4]
結果滿足要求的三元組集合爲:
[
[-1, 0, 1],
[-1, -1, 2]
]
算法如下:
public static List<List<Integer>> threeSum(int[] nums) {
if (nums.length < 3) return Collections.emptyList();
List<List<Integer>> res = new ArrayList<>();
int minValue = Integer.MAX_VALUE;//找到數組中最小值
int maxValue = Integer.MIN_VALUE;//找到最大值
int negSize = 0, posSize = 0;//negSize爲數組中負數的個數posSize爲數組中正數的個數
int zeroSize = 0;//數組中爲0的個數
for (int v : nums) {
if (v < minValue) minValue = v;
if (v > maxValue) maxValue = v;
if (v > 0) posSize++;
else if (v < 0) negSize++;
else zeroSize++;
}
if (zeroSize >= 3) res.add(Arrays.asList(0, 0, 0));//輸出 三個 0 的情況
if (negSize == 0 || posSize == 0) return res;
//此時minValue一定爲負數,maxValue一定爲正數
//如果maxValue > -2*minValue,那麼大於 -2*minValue的元素肯定不會是答案,可以排除掉,所以更新maxValue
if (minValue * 2 + maxValue > 0) maxValue = -minValue * 2;
//同理更新minValue
else if (maxValue * 2 + minValue < 0) minValue = -maxValue * 2;
//自己構建一個hashmap,值表示元素重複次數,注意java數組默認值爲 0
int[] map = new int[maxValue - minValue + 1];
int[] negs = new int[negSize];
int[] poses = new int[posSize];
negSize = 0;
posSize = 0;
for (int v : nums) {
if (v >= minValue && v <= maxValue) {//只保留在[minValue,maxValue]區間內的元素
if (map[v - minValue]++ == 0) {//計數加去重
if (v > 0) poses[posSize++] = v;//poses數組存所有去重後的正值
else if (v < 0) negs[negSize++] = v;//negs數組存所有去重後的負值
}
}
}
//正負數兩數組排序
Arrays.sort(poses, 0, posSize);
Arrays.sort(negs, 0, negSize);
int basej = 0;
for (int i = negSize - 1; i >= 0; i--) {//負數數組從後往前遍歷
int nv = negs[i];//nv爲當前負數值
//minp = -nv/2,相當於三元組中另外兩元素的平均值,即爲另兩個元素中較小值的上界,較大值的下界
int minp = (-nv) >>> 1;
//定位到正數數組中值剛好小於平均值的元素
while (basej < posSize && poses[basej] < minp) basej++;
for (int j = basej; j < posSize; j++) {//正數數組從大於等於平均值的元素開始遍歷
int pv = poses[j];//pv 爲當前正數值
int cv = 0 - nv - pv;//cv 爲要尋找的另一個值
//目標值 cv 應該在 [nv,pv] 當中
//如果不限制cv<=pv,當nv爲奇數時,有可能會重複輸出
if (cv >= nv && cv <= pv) {
if (cv == nv) {
if (map[nv - minValue] > 1)//兩個相同的負數和一個正數的情況
res.add(Arrays.asList(nv, nv, pv));
} else if (cv == pv) {
if (map[pv - minValue] > 1)//兩個相同的正數和一個負數的情況
res.add(Arrays.asList(nv, pv, pv));
} else {
if (map[cv - minValue] > 0)//三個不同元素的情況
res.add(Arrays.asList(nv, cv, pv));
}
} else if (cv < nv) break;//如果 cv 小於 nv了,表明這種情況會在後面尋找,爲避免重複輸出,跳出循環
}
}
return res;
測試
int [] nums = {-1, 0, 1, 2, -1, -4};
System.out.println(threeSum(nums));
結果
[[-1, 0, 1], [-1, -1, 2]]