Java陣列和排序
Java陣列和排序
陣列
概述
為什麼要有陣列
- 陣列的出現,是為了我們方便的去操做多個數據
陣列的概念
-
陣列,相當於一個容器,可以儲存一組,相同資料型別的元素
-
既可以儲存基本資料型別,也可以儲存引用資料型別
陣列宣告建立
陣列的定義格式
-
資料型別[] 陣列名:int[] a = new int[10];
-
資料型別 陣列名[]:double b[] = new double[10];
什麼是陣列的初始化
-
Java中的陣列必須先初始化,然後才能使用
-
所謂初始化:就是為陣列中的陣列元素分配記憶體空間,併為每個陣列元素賦值
初始化的分類
-
動態初始化:只指定長度,由系統給出初始化值
-
靜態初始化:給出初始化值,由系統決定長度
-
注意事項:兩種方式只能選擇其中之一
動態初始化的格式
-
資料型別[] 陣列名 = new 資料型別[陣列長度]
-
int[] a = new int[5];
靜態初始化的格式
-
資料型別[] 陣列名 = new 資料型別[]{元素1,元素2,……}
-
資料型別[] 陣列名 = {元素1,元素2,……}
-
int[] b = new int[]{2,3,4,5};
-
int[] b = {2,3,4,5};
陣列的常見操作
1、陣列元素遍歷
package com.tang.array; //陣列遍歷 public class ArrayDemo01 { public static void main(String[] args) { //定義陣列 int[] a = {2,4,5,6}; //遍歷陣列 for (int i = 0; i < a.length; i++) { System.out.println(a[i]); } } }
2、陣列常見的下標越界異常
ArrayIndexOutOfBoundsException
3、陣列元素的反向遍歷
package com.tang.array; //陣列反向遍歷 public class ArrayDemo02 { public static void main(String[] args) { //定義陣列 int[] a = {2,4,5,6}; //反向遍歷陣列 for (int i = a.length - 1; i >= 0; i--) { System.out.println(a[i]); } } }
4、獲取陣列中元素的最大值或最小值
package com.tang.array;
//獲取陣列中的最大值或最小值
public class ArrayDemo03 {
public static void main(String[] args) {
//定義陣列
int[] a = {45,34,5,23,97};
int max = a[0];
int min = a[0];
//遍歷陣列
for (int i = 0; i < a.length; i++) {
//最大值 max = max>a[i]?max:a[i];
max = Math.max(max, a[i]);
//最小值 min = min<a[i]?min:a[i];
min = Math.min(min, a[i]);
}
System.out.println("max: "+max);
System.out.println("min: "+min);
}
}
5、陣列元素的反轉
package com.tang.array;
import java.util.Arrays;
//陣列元素的反轉
public class ArrayDemo04 {
public static void main(String[] args) {
//定義陣列
int[] a = {25,34,5,23,97};
//方法一:
for (int i = 0; i < a.length-1-i; i++) {
//元素反轉
int t = a[i];
a[i] = a[a.length-1-i];
a[a.length-1-i]=t;
}
//方法二:
int[] b = new int[a.length];
for (int i = 0, j = b.length-1; i < a.length; i++, j--) {
b[j]=a[i];//元素反轉
}
a=b;
//輸出陣列元素
System.out.println(Arrays.toString(a));
}
}
二維陣列的介紹
1、二維陣列概述
- 二維陣列中的每個元素為一維陣列
2、二維陣列定義格式
二維陣列動態初始化
// 資料型別[][] 陣列名 = new 資料型別[m][n];
int[][] m = new int[4][3]; //動態初始化
// 資料型別 陣列名[][] = new 資料型別[m][n];
// 資料型別[] 陣列名[] = new 資料型別[m][n];
二維陣列靜態初始化
// 資料型別[][] 陣列名 = {{元素00,元素02,…},{元素10,元素12,…},{元素20,元素22,…}};
int[][] a = new int[][] {{1,3},{5,6},{3,4}}; //靜態初始化
3、二維陣列的遍歷
package com.tang.array.array2;
import java.util.Arrays;
//二維陣列
public class ArrayDemo01 {
public static void main(String[] args) {
int[][] a = new int[][]{{1,3},{5,6},{3,4}};
for (int i = 0; i < a.length; i++) {
System.out.println(Arrays.toString(a[i]));
}
System.out.println("====================");
System.out.println(Arrays.deepToString(a));
}
}
4、二維陣列的練習
案例演示:
1、需求:公司年銷售額求和
-
某公司按照季度和月份統計的資料如下:單位(萬元)
-
第一季度:22,66,44
-
第二季度:77,33,88
-
第三季度:25,45,65
-
第四季度:11,66,99
package com.tang.array.array2; public class ArrayDemo02 { public static void main(String[] args) { int sum = 0;//定義一個年銷售額變數:sum int[][] arr = {{22,66,44},{77,33,88},{25,45,65,},{11,66,99}}; //遍歷陣列,累加求和 for (int[] a : arr) { for (int i : a) { sum+=i; } } System.out.println("年銷售額: "+sum); } }
需求:列印楊輝三角形(行數可以鍵盤錄入)
package com.tang.array.array2;
import java.util.Scanner;
/*
需求:列印楊輝三角形(行數可以鍵盤錄入)
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
我們觀察發現:
1. 每一行的第一列和最後一列都是1
2. 從第三行開始,從第二列開始,這個數字等於他上一行的前一列和上一行的本列之和
*/
public class ArrayDemo03 {
public static void main(String[] args) {
System.out.println("請輸入行數: ");
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
//定義一個二維陣列,儲存資料
int[][] a = new int[n][n];
for (int i = 0; i < a.length; i++) {
a[i]=new int[i+1]; //設定每一行的元素個數
a[i][0] = 1; //第一列設定為1
a[i][i] = 1; //最後一列為設定為1
System.out.print(a[i][0]+" ");//列印每行第一列
//設定每行中間的元素值
for (int j = 1; j < a[i].length-1; j++) {
a[i][j]=a[i-1][j-1]+a[i-1][j]; //設定每行中間的元素值
System.out.print(a[i][j]+" ");
}
System.out.print(a[i][i]);//列印每行最後一列
System.out.println();
}
}
}
需求:查詢陣列元素的索引
-
方式1:遍歷陣列挨個查詢
package com.tang.array.array2; /* 需求:根據陣列元素查找出該元素第一次在陣列中出現的索引 - 方式1:遍歷陣列挨個查詢--->第一次索引 - 方式2:二分查詢--->不一定是第一次索引 */ public class ArrayDemo04 { public static void main(String[] args) { //定義一個數組 int[] a = {56,34,63,4,34,3,32}; //根據陣列元素查找出該元素第一次在陣列中出現的索引 int index = getIndexByEle(a,34); System.out.println("該元素第一次在陣列中出現的索引是: "+index); } //方式1:遍歷陣列挨個查詢 private static int getIndexByEle(int[] a, int ele) { for (int i = 0; i < a.length; i++) { if(ele==a[i]){ return i; } } return -1;//沒有找到就返回-1 } }
-
方式2:二分查詢
-
二分查詢的前提:陣列必須是有序的
-
二分查詢的思想:每次查詢中間的元素,比較大小就能減少一半的元素
package com.tang.array.array2; /* 需求:根據陣列元素查找出該元素第一次在陣列中出現的索引 - 方式2:二分查詢--->不一定是第一次索引 */ public class ArrayDemo05 { public static void main(String[] args) { //定義一個數組 int[] a = {1,2,3,4,5,6,7,8,9}; //根據陣列元素查找出該的索引 int index = getIndexByEle(a,3); System.out.println("該元素在陣列中出現的索引是: "+index); } //方式2:二分查詢 private static int getIndexByEle(int[] a, int ele) { //定義最小索引 int min = 0; //定義最大索引 int max = a.length-1; for (int i = (min+max)/2; min <= max; i = (min+max)/2) { //找到元素,返回索引 if(ele==a[i]){ return i; // ele>中間索引元素,min索右移,等於中間索引+1 }else if(ele>a[i]){ min=i+1; // ele<中間索引元素,max索引左移,等於中間索引-1 }else{ max=i-1; } } return -1;//沒有找到就返回-1 } }
-
陣列之常見排序演算法
排序:把一個無需序列通過某種方式變成一個有序序列
1.氣泡排序
-
排序原理:陣列元素兩兩比較,互動位置,大元素往後放,那麼經過 一輪比較後,最大的元素,就會出現在最大索引處
-
int[] arr = {24, 69, 80, 57, 13}; //待排序陣列
package com.tang.sort; import java.util.Arrays; public class ArraysDemo01 { public static void main(String[] args) { int[] arr = {24, 69, 80, 57, 13};//待排序陣列 boolean flag = true;//標記位,可以減少比較次數 for (int i = 0; i < arr.length-1; i++) { for (int j = 1; j < arr.length-i; j++) { if (arr[j-1]>arr[j]){ //交換位置 int t = arr[j-1]; arr[j-1] = arr[j]; arr[j] = t; flag = false;//標記位,可以減少比較次數 } } if (flag){ break; } flag = true; } System.out.println(Arrays.toString(arr)); } }
2.選擇排序
-
排序原理:從0索引處開始,依次和後面的元素進行比較,小的元素往前放,經過一輪比較後,最小的元素就出現在了最小索引處
-
int[] arr = {24, 69, 80, 57, 13}; //待排序陣列
package com.tang.sort; import java.util.Arrays; public class ArraysDemo02 { public static void main(String[] args) { int[] arr = {24, 69, 80, 57, 13};//待排序陣列 for (int i = 0; i < arr.length-1; i++) { for (int j = i+1; j < arr.length; j++) { if (arr[i]>arr[j]){ //交換位置 int t = arr[i]; arr[i] = arr[j]; arr[j] = t; } } } System.out.println(Arrays.toString(arr)); } }
3.插入排序
-
排序原理:演算法思路:直接插入排序,是一種最簡單的排序方法,它的基本操作是將一個記錄插入到一個長度為m的有序表中,使之仍保持有序
package com.tang.sort; import java.util.Arrays; //直接插入排序 public class ArraysDemo03 { public static void main(String[] args) { int[] arr = {24, 69, 80, 57, 13};//待排序陣列 //外層迴圈定義輪次 for (int i = 1; i < arr.length; i++) { //裡層迴圈進行比較插入 for (int j = i; j > 0; j--) { if (arr[j]<arr[j-1]){ int t = arr[j]; arr[j] = arr[j-1]; arr[j-1] = t; }else { break; } } } System.out.println(Arrays.toString(arr)); } }
4.希爾排序
-
希爾排序又稱縮小增量排序,是對插入排序的一個優化
-
基本思想:先將原表按增量ht分組,每個子檔案按照直接插入法排序。同樣,用下一個增量ht/2將檔案再分為子檔案,再直接插入法排序。直到ht=1時整個檔案排好序
-
關鍵:選擇合適的增量
-
增量選擇:
-
陣列長度的一半,效率不是太高
-
Knuth序列
-
h=1
-
h=3*h+1
-
-
-
-
希爾排序演算法:可以通過三重迴圈來實現
package com.tang.sort; import java.util.Arrays; //希爾排序 public class ArraysDemo04 { public static void main(String[] args) { int[] arr = {46, 55, 13, 42, 17, 94, 5, 70}; shellSort(arr); System.out.println(Arrays.toString(arr)); } private static void shellSort(int[] arr) { //定義一個增量 int h = 1; //根據克努特序列選取我們的第一次增量 while (h <= arr.length/3){ h = h*3+1; } for (; h > 0; h/=2) { for (int i = h; i < arr.length; i++) { //裡層迴圈進行比較插入 for (int j = i; j >= h; j-=h) { if (arr[j]<arr[j-h]){ int t = arr[j]; arr[j] = arr[j-h]; arr[j-h] = t; }else { break; } } } } } }
插入排序與希爾排序比較:
- 使用希爾排序,元素位置交換的次數變少了,效率變高了
5.快速排序
-
分治法:比大小,再分割槽
-
從陣列中取出一個數,作為基準數
-
分割槽:將比這個數大或等於的數全放到他的右邊,小於它的數全放到它的左邊
-
再對左右區間重複第二步,直到個區間只有一個數
-
-
實現思路
-
將基準數挖出形成第一個坑
-
由後向前找比他小的數,找到後挖出次數填到前一個坑中
-
由前向後找比他大或等於的數,找到後也挖出此數填坑到前一個坑中
-
再重複下執行2,3兩步驟
package com.tang.sort; import java.util.Arrays; //快速排序 public class ArraysDemo05 { public static void main(String[] args) { int[] arr = {46, 55, 13, 42, 17, 94, 5, 70}; int start = 0; int end = arr.length-1; quickSort(arr, start, end); System.out.println(Arrays.toString(arr)); } //快速排序 private static void quickSort(int[] arr, int start, int end) { //找出左右兩區的索引位置,然後對左右兩區進行遞迴呼叫 if (start<end){ //找出左右兩區的索引位置 int index = getIndex(arr,start,end); //左邊快速排序 quickSort(arr, start, index-1); //右邊快速排序 quickSort(arr, index+1, end); } } /* 1. 將基準數挖出形成第一個坑 2. 由後向前找比他小的數,找到後挖出次數填到前一個坑中 3. 由前向後找比他大或等於的數,找到後也挖出此數填坑到前一個坑中 4. 再重複下執行2,3兩步驟 */ //獲取一輪排序後的基數最終索引 private static int getIndex(int[] arr, int start, int end) { int i = start; int j = end; int x = arr[i]; //基準數 while (i<j){ while (i<j&&arr[j]>=x){ j--; } if (i<j){ arr[i]=arr[j]; } while (i<j&&arr[i]<x){ i++; } if (i<j){ arr[j]=arr[i]; } } arr[i]=x; return i; } }
-
6.歸併排序
演算法思想:
-
歸併排序(Merge Sort)就是利用歸併的思想實現排序的方法。
-
它的原理是假設初始序列有N個記錄,可以看成是N個有序的子序列,每個子序列的長度為1,然後兩兩歸併你…如此重複,直至得到一個長度為N的有序序列為止,這種排序方法稱為2路歸併排序。
package com.tang.sort;
import java.util.Arrays;
//歸併排序
public class ArraysDemo06 {
static int[] b;
public static void main(String[] args) {
int[] arr = {46, 55, 13, 42, 17, 94, 5, 70};
//拆分
split(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
//拆分
private static void split(int[] arr, int start, int end) {
//計算中間索引
int center = (start + end) / 2;
if (start<end){
//拆分左邊
split(arr, start, center);
//拆分右邊
split(arr, center+1, end);
//歸併
merge(arr, start, center, end);
}
}
//歸併
private static void merge(int[] arr, int start, int center, int end) {
//定義一個臨時陣列
int[] b = new int[end-start+1];
//定義左邊陣列的起始索引
int i =start;
//定義右邊陣列的起始索引
int j =center+1;
//定義臨時陣列的起始索引
int index = 0;
//比較左右兩邊陣列的元素大小,往臨時陣列中放
while (i<=center&&j<=end){
if (arr[i]<=arr[j]){
b[index]=arr[i];
i++;
}else {
b[index]=arr[j];
j++;
}
index++;
}
//處理剩餘元素
while (i<=center){
b[index]=arr[i];
i++;
index++;
}
while (j<=end){
b[index]=arr[j];
j++;
index++;
}
//將臨時陣列中的元素取到原陣列中
for (int k = 0; k < b.length; k++) {
arr[k+start]=b[k];
}
}
}
7.基數排序
-
基數排序不同於之前所介紹的各類排序
-
前邊介紹到的排序方法或多或少的是通過使用比較和移動記錄來實現排序,而基數排序的實現不需要進行對關鍵字的比較,只需要對關鍵字進行”分配“與”收集“兩種操作即可完成。
package com.tang.sort; import java.util.Arrays; //基數排序 public class ArraysDemo07 { public static void main(String[] args) { //基數排序: 通過分配再收集的方式進行排序 int[] arr = {33,24,8,876,34,12,67,89,56,43,354,45,34}; //基數排序 sort(arr); System.out.println(Arrays.toString(arr)); } //基數排序 private static void sort(int[] arr) { //定義二維陣列管理,放10個桶 int[][] tempArr = new int[10][arr.length]; //定義一維陣列為上面10個桶統計數字 int[] counts = new int[10]; //獲取陣列中的最大值 int max = getMax(arr); //確定排序輪次 int len = String.valueOf(max).length();//獲取整數的位數 for (int i = 0, n = 1; i < len; i++, n*=10) { //元素放入桶中 for (int j = 0; j < arr.length; j++) { //獲取每個數各個位上的數字 int ys = arr[j] / n % 10; tempArr[ys][counts[ys]++] = arr[j]; } //取出桶中的元素 //定義arr陣列的下標 int index = 0; for (int k = 0; k < 10; k++) { if (counts[k]!=0) { for (int h = 0; h < counts[k]; h++) { //從桶中取出元素放回原陣列 arr[index] = tempArr[k][h]; index++; } counts[k]=0;//清楚上一次統計的個數 } } } } //獲取陣列中的最大值 private static int getMax(int[] arr) { int max = arr[0]; for (int i = 1; i < arr.length; i++) { if (arr[i]>max){ max=arr[i]; } } return max; } }
8.堆排序
-
堆排序是利用堆這種資料結構而設計的一種排序演算法,堆排序是一種選擇排序
-
堆排序的基本思想:
-
將待排序序列構造成一個大頂堆,此時,整個序列的最大值就是堆頂的根節點
-
將其與末尾元素進行交換,此時末尾就為最大值
-
然後將剩餘n-1個元素重新構造成一個堆,這樣會得到n個元素的次小值
-
如此反覆執行,便能得到一個有序序列了
-
-
我們用簡單的公式來描述一下堆排序的定義就是:
-
大頂堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
-
小頂堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
package com.tang.sort; import java.util.Arrays; //堆排序 public class ArraysDemo08 { public static void main(String[] args) { //定義一個數組 int[] arr = {33,24,8,876,34,12,67,89,56,43,354,45,34}; //調成大頂堆的方法 //定義開始調整的位置 int startIndex = arr.length/2-1; //迴圈開始調 for (int i = startIndex; i >= 0; i--) { getMaxHeap(arr,arr.length,i); } //經過上面的操作後,已經把陣列變成一個大頂堆,把根元素和最後一個元素進行調換 for (int i = arr.length-1; i > 0; i--) { //進行調換 int t = arr[0]; arr[0] = arr[i]; arr[i] = t; //換完之後我們再把剩餘元素調成大頂堆 getMaxHeap(arr,i,0); } System.out.println(Arrays.toString(arr)); } /** * * @param arr 要排序的陣列 * @param size 調整的元素個數 * @param index 從哪裡開始調整 */ private static void getMaxHeap(int[] arr, int size, int index) { //獲取左右子節點的索引 int leftNodeIndex = index*2+1; int rightNodeIndex = index*2+2; //找到最大節點所對應的索引 int maxIndex = index; if (leftNodeIndex<size&&arr[leftNodeIndex]>arr[maxIndex]){ maxIndex = leftNodeIndex; } if (rightNodeIndex<size&&arr[rightNodeIndex]>arr[maxIndex]){ maxIndex = rightNodeIndex; } //我們來調換位置 if (maxIndex!=index){ int t = arr[maxIndex]; arr[maxIndex] = arr[index]; arr[index] = t; //調換完之後,可能會影響到,下面的子樹,不是大頂堆,我們還需要再次調換 getMaxHeap(arr,size,maxIndex); } } }