資料結構之陣列與經典面試題
1、定義
所謂陣列,是有序的元素序列。
若將有限個型別相同的變數的集合命名,那麼這個名稱為陣列名。組成陣列的各個變數稱為陣列的分量,也稱為陣列的元素,有時也稱為下標變數。用於區分陣列的各個元素的數字編號稱為下標。陣列是在程式設計中,為了處理方便,
把具有相同型別的若干元素按無序的形式組織起來的一種形式。這些無序排列的同類資料元素的集合稱為陣列。int 的陣列你就不能存float
也不能存double。陣列是用於儲存多個相同型別資料的集合。通常用Array表示,也稱之為線性表,畫圖演示
2、特點
(1)陣列是相同資料型別的元素的集合。 (2)陣列中的各元素的儲存是有先後順序的,它們在記憶體中按照這個先後順序連續存放在一起。記憶體地址
(3)陣列元素用整個陣列的名字和它自己在陣列中的順序位置來表示。例如,a[0]表示名字為a的陣列中的第一個元素,a[1]代表陣列a的第二個元素,以此類推,(4)隨機訪問(查詢)
陣列是連續的記憶體空間和相同型別的資料。正是因為這兩個限制,它才有了一個非常重要的特性:隨機訪問。但有利就有弊,這兩個限制也讓陣列的很多操作變得非常低效,比如要想在陣列中刪除、插入一個數據,為了保證連續性,就需要做大量的資料搬移工作。
(5)陣列的缺點:插入和刪除 實現程式碼:
設陣列的長度為n,現在,如果我們需要將一個數據插入到陣列中的第k個位置。刪除第N個位置的資料.那麼需要移動刪除和插入後面的陣列元素
(6)使用陣列一定要注意訪問越界問題;所以一定要多加判斷,尤其是在開始和結束。測試的時候也一樣注意頭和尾。
3.表現形式
(1)一維陣列
int a[],String a[] (2)多維陣列
int a[][],int a[][][]。 int a[m][n]:記憶體空間是多少? m*n a[0][10]:
連結串列解決,a[0]:->10>2 a[1]->15
4、ArrayList和陣列
本質是一樣的,都是陣列。ArrayList是JDK封裝了。不需要管擴容等操作;陣列的話就要你全部操作 兩者選擇:
不知道資料大小的肯定選ArrayList。
如果你知道資料的大小而且你又非常關注效能那就用陣列。
5、二維陣列的記憶體地址是怎麼樣的?寫出定址公式?
一維:a[] = new int[10]; ==> loc =
init_loc(初始記憶體地址)+index(陣列的下標)size(資料的長度) 二維=>轉化一維 1 2 3 4 5 6 =>
1 2 3 4 5 6 => 4的下標在二維裡面是 (1 ,0) =>在一維裡面是第3個。=> in(一維的長度)+j(在列
)=>13+0=3 a[i][j]: (i<n,j<m) loc=init_loc+(in+j)*size
6、總結:
陣列是一個最基礎最簡單的資料結構,必須要完全搞懂。它是儲存相同型別的一組資料,最大的兩個特點就是下標和隨機訪問。缺點就是插入和刪除是很慢的,時間複雜度為O(n)。
7、經典面試
7.1 、例子:手寫ArrayList
/**
* @author zhz
* @date 2020/11/11
**/
public interface MyList {
void add(int index, Object element);
int size();
void add(Object element);
void remove(int index);
Object get(int index);
String toString();
void ensureCapacityInternal();
}
import com.hr.tuling.array.MyList;
import java.util.Arrays;
/**
* @author zhz
* @date 2020/11/11
**/
public class MyArrayList implements MyList {
/**
* 定義一個數組,用於儲存集合中的資料
*/
private Object[] elementData;
/**
* 定義一個變數,用於儲存陣列中實際存放元素的個數
*/
private int size;
/**
* 獲取陣列中實際存放元素的個數
*
* @return
*/
public int size() {
return this.size;
}
/**
* 無參構造方法(預設設定elementData陣列的空間長度為10)
*/
public MyArrayList() {
this.elementData = new Object[10];
}
/**
* 有參構造方法(指定設定elementData陣列的空間長度)
*
* @param cap 需要設定elementData的空間長度
*/
public MyArrayList(int cap) {
//1、判斷cap是否合法
if (cap < 0) {
throw new RuntimeException("引數不合法,cap:" + cap);
}
//2、例項化elementData陣列
this.elementData = new Object[cap];
}
/**
* 新增元素
*
* @param element 需要新增的元素
*/
@Override
public void add(Object element) {
//1、判斷陣列是否需要擴容
ensureCapacityInternal();
//2、把element新增進入陣列中
elementData[size] = element;
//3、更新size的值
size++;
}
/**
* 根據索引獲取元素值
*
* @param index 索引值
* @return 陣列中index索引對應的元素值
*/
@Override
public Object get(int index) {
//1、判斷索引是否合法,合法的取值範圍:【0,size-1】
rangeCheck(index);
//2、根據索引獲取對應的元素值
return elementData[index];
}
/**
* 根據索引刪除元素
*
* @param index 索引值
*/
@Override
public void remove(int index) {
//1、判斷索引是否合法,合法的取值範圍:【0,size-1】
rangeCheck(index);
//2、把刪除索引之後的元素往前移動一位
//2.1先獲得刪除索引及其之後的所有索引值
for (int i = index; i < size; i++) {
//2.2把後一個元素往前移動一位
elementData[i] = elementData[i + 1];
}
//3、把最後一個實際新增的元素設定為預設值
elementData[size - 1] = null;
}
/**
* 根據索引插入元素
*
* @param index 插入元素的索引位置
* @param element 需要插入的元素
*/
@Override
public void add(int index, Object element) {
//1、判斷索引是否合法,合法的取值範圍:【0,size】-->插入的元素可以在實際新增元素的最末尾
if (index < 0 || index > size) {
throw new ArrayIndexOutOfBoundsException("索引越界異常,index:" + index);
}
// 2.判斷陣列是否需要擴容
ensureCapacityInternal();
// 3.插入索引及其之後的元素往後挪動一位(從後往前挪動)
// 3.1獲得插入索引及其之後的所有索引值
for (int i = size - 1; i >= index; i--) {
// 3.2把前一個元素往後挪動一位
elementData[i + 1] = elementData[i];
}
// 4.在插入索引位置實現賦值操作
elementData[index] = element;
// 5.更新size的值
size++;
}
/**
* 檢查索引是否合法(get和remove)
*
* @param index
*/
private void rangeCheck(int index) {
//1、判斷索引是否合法,合法的取值範圍:【0,size-1】
if (index < 0 || index >= size) {
throw new ArrayIndexOutOfBoundsException("索引越界異常,index:" + index);
}
}
/**
* 判斷陣列是否需要執行擴容操作
*/
public void ensureCapacityInternal() {
//1.1、當陣列的空間長度等於陣列實際存放元素的個數時,這時就需擴容擴容操作
if (elementData.length == size) {
//1.2、建立一個比原陣列空間長度更大的新陣列
Object[] newArr = new Object[elementData.length * 2 + 1];
//1.3、把原陣列中的元素拷貝進入新陣列中
for (int i = 0; i < size; i++) {
newArr[i] = elementData[i];
}
//1.4、讓原陣列儲存新陣列的地址值
elementData = newArr;
}
}
@Override
public String toString() {
return "ArrayList{" +
"elementData=" + Arrays.toString(elementData) +
'}';
}
public static void main(String[] args) {
MyList myList = new MyArrayList(7);
myList.add(2);
myList.add(3);
myList.add(2);
myList.add(3);
System.out.println(myList.get(2));
}
}
7.2、陣列的反轉
import java.util.Arrays;
/**
* 陣列的反轉
* 例如:陣列{11, 22, 33, 44, 55, 66}反轉後為{66, 55, 44, 33, 22, 11}
* @author zhz
* @date 2020/10/03
**/
public class 陣列的反轉 {
/**
* 方法一:
* 1)新建陣列,用於儲存反轉後的結果
* 2)把需要反轉陣列中的元素倒序存入新陣列中
*/
public static int[] reverseOrderArray(int[] arr){
// 1.定義一個新陣列,用於儲存反轉之後的結果
int[] temp=new int[arr.length];
// 2.把arr陣列中的所有元素倒序的存入temp陣列中
// 2.1通過迴圈獲得arr陣列中的每一個元素
for (int i = arr.length-1; i >= 0; i--) {
// 2.2把arr陣列中的元素倒序存入temp陣列中
temp[arr.length-1-i]=arr[i];
}
// 3.把反轉之後的陣列返回
return temp;
}
/**
* 方法二:把需要反轉陣列的元素首尾交換即可
*/
public static void reverseOrderArray1(int[] arr){
// 1.通過迴圈,獲得陣列前半部分的元素
for (int i = 0; i <arr.length/2; i++) {
// 2.把arr[i]和arr[arr.length - 1 - i]做交換
int temp=arr[i];
arr[i]=arr[arr.length-1-i];
arr[arr.length-1-i]=temp;
}
}
public static void main(String[] args) {
int[] arr={11,22,33,44,55,66};
//System.out.println(Arrays.toString(陣列的反轉.reverseOrderArray(arr)));
陣列的反轉.reverseOrderArray1(arr);
System.out.println(Arrays.toString(arr));
}
}
7.3、 找陣列中重複的元素
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author zhz
* @date 2020/10/03
**/
public class 找陣列中重複的元素 {
/**
* 問題:在一個長度為n的數組裡的所有數字都在0到n-1的範圍內。 陣列中某些數字是重複的,
* 但不知道有幾個數字是重複的。也不知道每個數字重複幾次。請找出陣列中任意一個重複的數字。
* 例如,如果輸入長度為7的陣列{2,3,1,0,2,5,3},那麼對應的輸出是第一個重複的數字2
*
* 解決:第一種,用map做計數器,當大於1時,則返回true,否則false
*/
public static boolean duplicate(int numbers[],int length,int [] duplication) {
Map<Integer, Integer> map = new HashMap<>();
int count=1;
// 1.判斷arr為null或arr.length等於0的情況
if(numbers == null || length == 0) {
return false;
}
for (int i = 0; i < length; i++) {
// 3.判斷陣列元素是否合法
if(numbers[i] < 0) {
return false;
}
if (!map.containsKey(numbers[i])){
map.put(numbers[i],count);
}else{
map.put(numbers[i],map.get(numbers[i])+1);
}
}
for (Map.Entry<Integer,Integer> entry : map.entrySet()){
if (entry.getValue()>1){
duplication[0]=entry.getKey();
return true;
}
}
return false;
}
/**
* 題目:在一個長度為 n 的陣列 nums 裡的所有數字都在 0~n-1 的範圍內。數
* 組中某些數字是重複的,但不知道有幾個數字重複了,也不知道每個數字重複了幾次。
* 請找出陣列中任意一個重複的數字。
*
* 解決:用set去判重
*/
public int findRepeatNumber(int[] nums) {
Set<Integer> set=new HashSet<>();;
int res=0;
for (int num : nums) {
if (!set.contains(num)) {
set.add(num);
}else {
res=num;
}
}
return res;
}
public static void main(String[] args) {
找陣列中重複的元素.duplicate(new int[]{
0, 3, 4, 1, 4, 8
},6,new int[]{1});
}
}
7.4、使奇數位於偶數前面
import java.util.Arrays;
/**
* 使奇數位於偶數前面
* 輸入一個整型陣列,實現一個方法來調整該陣列中的元素的順序,
* 使得所有奇數位於陣列的前半部分,所有偶數位於陣列的後半部分。
* @date 2020/10/03
**/
public class 使奇數位於偶數前面 {
/**
* 使奇數位於偶數前面
* @param array 需要調整奇偶數位置的陣列
*/
public static void reOrderArray(int[] array) {
// 1.處理arr為null的情況
if (array == null) {
throw new NullPointerException("空指標異常,array:" + array);
}
// 2.定義兩個下標,min的初始值為0,max的初始值為arr.length - 1
int min = 0;
int max = array.length - 1;
// 3.定義一個迴圈,用於調整陣列中奇偶數的位置
while (min < max) {// 如果min小於max,則一直調整陣列中元素的位置
// 4.讓min從前往後找,如果arr[min]的值為偶數,則停止查詢
while (min < max && array[min] % 2 != 0) {
min++;
}
// 5.讓max從後往前找,如果arr[max]的值為奇數,則停止查詢
while (min < max && array[max] % 2 == 0) {
max--;
}
// 6.如果min的值不等於max,則交換arr[min]和arr[max]的值
if (min != max) {
int temp = array[min];
array[min] = array[max];
array[max] = temp;
}
}
}
/**
*新開一個數組空間
* @param nums
* @return
*/
public int[] exchange(int[] nums) {
if (nums==null||nums.length==0){
return nums;
}
int left=0;
int right=nums.length-1;
int[] res=new int[nums.length];
for (int i = 0; i < nums.length; i++) {
if ((nums[i]&1)==0){//偶數
res[right--]=nums[i];
}else{
res[left++]=nums[i];
}
}
return res;
}
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9};
使奇數位於偶數前面.reOrderArray(array);
System.out.println(Arrays.toString(array));
}
}