對數組操作最基本的動作就是 : 存,取
核心思想:對角標的操作
int[] arr = {89,35,56,75};
1.遍歷:
數組的長度:arr.length
數組的最大角標:數組的長度 - 1
for(int i=0; i < arr.length; i++) //對角標正向的操作
{
System.out.println("arr[" + i + "] = " + arr[i]);
}
for(int i = arr.length-1; i >= 0; i--) //對角標反向的操作
{
System.out.println("arr[" + i + "] = " + arr[i]);
}
2.最值:
思路:定義變量記錄較大的值或者 較大值的角標,然後遍歷數組比較。
static int max(int[] arr)
{
int maxElement = arr[0]; //首個元素作爲初始化值
for(int i=1; i < arr.length; i++)
{
if(arr[i] > maxElement)
maxElement = arr[i];
}
return maxElement;
}
static int max(int[] arr)
{
int maxIndex = 0; //角標作爲初始化值
for(int i=1; i < arr.length; i++)
{
if(arr[i] > arr[maxIndex])
maxIndex = i;
}
return arr[maxIndex];
}
(選擇排序和冒泡排序 面試 用,開發的時候直接用Arrays.sort(arr); ,在類java.util.*中,默認從小到大排序。其他語言不一定有,還是要自己實現 )
3.選擇排序:從左到右依次選出後面的最小或者最大的值。是從前面開始完成。
static void selectSort(int[] arr)
{
for(int i = 0; i < arr.length-1; i++) //-1是因爲最後一次循環多餘
{
for(int j = i+1; j< arr.length; j++) //=i+1 是因爲選擇排序不再和前面的元素比較了,前面的元素已經排好了。
{
if(arr[i] > arr[j])
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
4.冒泡排序:兩兩相鄰比較,把最大或者最小的冒泡到最後。是從最後開始完成。
static void bubbleSort(int[] arr)
{
for(int i = 0; i < arr.length-1; i++) //-1是因爲最後一次循環多餘
{
for(int j = 0; j < arr.length-1-i; j++) //-1是因爲後面的j+1 也要小於 arr.length,
//-i是因爲每往後循環一次,後面排好的元素就多一個,比較的次數也相應減少一次
{
if(arr[j] < arr[j+1])
{
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
//簡化版:
static void bubbleSort(int[] arr)
{
for(int i = arr.length-1; i > 0 ; i--)
{
for(int j = 0; j < i; j++) //利用i的變化簡化內循環
{
if(arr[j] < arr[j+1])
{
swap(arr, j, j+1); //代碼複用
/*int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;*/
}
}
}
}
//排序位置置換代碼提取
static void swap(int[] arr, int a, int b) //必須帶入數組的地址
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
5.排序性能問題:
1.選擇排序:幾次比較換位才確定一個最值,效率低。
解決思路:比較但是不直接換位,通過變量記錄最值的角標和對應的值,最後把變量的值一次賦值給前面的元素。此時只進行了一次換位,中間過程原實體的數據是不變的。
寫的過程中犯的錯:
沒有記錄比較過程中的最值的中間值,導致每一次都是和外循環的第一個元素比較,
互換的只是最後一個比首元素小的元素,不是最小的元素。
代碼:
static void selectSort(int[] arr)
{
for(int i = 0; i < arr.length-1; i++)
{
int index = i; //記錄最值的角標
int num = arr[i]; //記錄最值,初始化爲第一個參與比較的元素,參與內循環
for(int j = i+1; j< arr.length; j++)
{
if(num > arr[j]) //通過最值變量依次參與比較,找到更小的值
index = j; //更新最值角標,找到最終的最值角標,參與最後的換位
num = arr[j]; //更新最值,參與下次循環比較
}
if(i != index)
swap(arr,i,index); //將找到的最值與原實體最值的位置位置。選擇排序,是從最前開始排序。
}
}
2.冒泡排序:同樣的道理,
優化:通過變量記錄最值的角標和對應的值,最後把變量的值一次賦值給後面的元素。減少換位次數。
代碼:
static void bubbleSort(int[] arr)
{
for(int i = 0; i < arr.length-1; i++)
{
int index = arr.length -1-i; //記錄最值的角標
int num = arr[0]; //記錄最值,初始化爲第一個參與比較的元素,參與內循環
for(int j = 0; j < arr.length-1-i; j++)
{
if(num < arr[j+1])
{
index = j+1;
num = arr[j+1];
}
}
if(index != arr.length -i)
swap(arr,arr.length-1-i,index); //將找到的最值與原實體最值的位置位置。冒泡是從最後開始排序。
}
}
//優化簡化版:
static void bubbleSort(int[] arr)
{
for(int i = arr.length-1; i > 0 ; i--)
{
int index = i;
int num = arr[0];
for(int j = 0; j < i; j++) //利用i的變化簡化內循環
{
if(num < arr[j+1])
{
index = j+1;
num = arr[j+1];
}
}
if(index != i)
swap(arr,i,index);
}
}
(二分查找代碼 面試 用,Java開發的時候直接用Arrays.binarySearch(arr,57); ,在類java.util.*中。其他語言不一定有,還是要自己實現。 )
關於Arrays.binarySearch() :
1.元素存在時,返回的是元素的角標,即mid。
2.當查找的元素不存在時,返回的是 - 插入點 - 1( - min -1)。返回負數是因爲區分元素不存在時插入點,-1是防止元素不存在而插入點爲0時,這時-1變成負數用以說明元素不存在。
3.如果折半查找的元素在數組裏有重複的,則返回哪個元素的角標視所處位置而定。
6.折半查找:前提數組是有序的。最終確定中間元素等於目標元素的角標。
/*static int halfSearch(int[] arr, int x)
{
int min = 0;
int max = arr.length -1;
int mid = (min + max) / 2;
while(x != arr[mid])
{
if(min > max) //元素不存在
return -1;
else if(x > arr[mid])
min = mid + 1;
else
max = mid - 1;
mid = (min + max) / 2;
}
return mid;
}
}*/
//另一種表示:簡化點
static int halfSearch(int[] arr, int x)
{
int min, max, mid;
int min = 0;
int max = arr.length -1;
while(min < max) //元素存在
{
mid = (min + max) >>1; //右移
if(x == arr[mid])
return mid;
else if(x > arr[mid])
min = mid + 1;
else
max = mid - 1;
}
return -1; //Arrays.binarySearch() 返回的是 - 插入點 - 1( -min-1)
}
}
面試題:
給定一個有序的數組,如果往該數組中存儲一個元素,並保證這個數組還是有序的,那麼這個元素的存儲的角標如何獲取。
static int getIndex(int[] arr, int x)
{
int min, max, mid;
int min = 0;
int max = arr.length -1;
while(min < max) //元素存在
{
mid = (min + max) >>1; //右移
if(x == arr[mid])
return mid; //返回元素角標
else if(x > arr[mid])
min = mid + 1;
else
max = mid - 1;
}
return min; //元素不存在時,返回插入點。Arrays.binarySearch() 返回的是 - 插入點 - 1( -min-1)
}
}
7.應用:進制轉換:
Demo:
//位轉換,8次。如何根據不同的數減少循環次數,是個問題。
class toHexDemo
{
public static void main(String[] args)
{
toHex(0);
toBin(-2147483648);
}
static void toHex(int num)
{ //開發思想:數據一多,就存儲起來,在進行操作。
int[] hex = new int[8]; //定義一個臨時數組,按一定順序存放十六進制的位值,大於10的最後輸出的時候再進行格式輸出
int x = 7; //找出十六進制開始第一個不爲0的角標,再進行遍歷輸出。(也就是消去了開始的無效0)
//默認爲7,是爲了避免當輸入的十進制爲0的時候,沒有不爲0 的數,則直接輸出最後一位0
//即0的十六進制數爲0
for(int i = 0; i < 8; i++) //位轉換,8次。如何根據不同的數減少循環次數,是個問題。
{
int n = num>>>(4*i)&15; //移位用>>>比較好,負數不用補有效位1
hex[7-i] = n; //得到的十六進制數字倒序存入數組
}
for(int i = 0; i < 8; i++) //找到x的具體值
{
if(hex[i] != 0)
{
x = i;
break;
}
}
System.out.println(num + "\t對應的十六進制表現形式是:");
for(int i = x; i < 8; i++) //輸出對應的完整的有效十六進制數。
{
if(hex[i] < 10)
System.out.print(hex[i]);
else
System.out.print((char)(hex[i] - 10 + 'A'));// 也可以(char)(n+55),把數值轉換成十六進制對應的字母。
//或者使用數組查表法
//即char[] chs = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
//System.out.print(chs[hex[i]]);
/* 解釋:
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
0,1,2,3,4,5,6,7,8,9,A, B, C, D, E, F
什麼時候使用數組呢?
如果數據出現了對應關係,而且對應關係的一方是有序的數字編號。並作爲角標使用,
這時就必須想到用數組。
就可以將這些數據存儲到數組中。
根據運算的結果作爲角標直接去查數組中對應的元素即可。
(當對應關係的一方不是有序的數字編號時,使用的是Map集合。)
*/
}
System.out.println();
}
//同理轉換二進制:
static void toBin(int num)
{
int[] bin = new int[32];
int x = 31;
for(int i = 0; i < 32; i++)
{
int n = num>>>(1*i)&1;
bin[31- i] = n;
}
for(int i =0; i < 32; i++)
{
if(bin[i] != 0)
{
x = i;
break;
}
}
System.out.println(num + "\t對應的二進制表現形式是:");
for(int i=x; i<32; i++)
{
System.out.print(bin[i]);
}
System.out.println();
}
}
最終程序源代碼:
class HexConverter
{
public static void main(String[] args)
{
//輸入優化
int hexFront =16;
int hexAfter = 10;
String numFront = "8FFFFFFF";
System.out.println(hexFront + "進制\t\t轉換\t\t" + hexAfter + "進制\n");
System.out.println(numFront + "\t\t\t\t" + hexConverter(hexFront, hexAfter, numFront));
}
static String hexConverter(int hexFront, int hexAfter, String numFront)
{
if(hexFront == 10)
return decTo(hexAfter, numFront);
else if(hexAfter == 10)
return toDec(hexFront, numFront);
else
return decTo(hexAfter, toDec(hexFront, numFront));
}
//十進制轉換其他進制,算法是位運算,可直接處理負數,負數的二進制是對應正數二進制取反加一。
//實際開發:用Integer.toBinaryString(-6)等等...
static String decTo(int hexAfter, String numFront)
{
//需要優化:判斷字符串是否是數字,是否超出被轉換數類型最大範圍。這裏爲int
int num = Integer.parseInt(numFront);
StringBuffer sb = new StringBuffer();
//限制:進制數爲常見的2的冪方,1,2,4,8,16,32...,保證位運算位數爲整數
//解決辦法:想取消這個限制,可以考慮短除法思想,重寫算法,
int byteNum = (int)(Math.log((double)hexAfter)/Math.log((double)2)); //位運算位數
int resultNum =(int) (Math.ceil(32/byteNum)); //結果位數
int x = resultNum -1; //找出第一個不爲0的角標
for(int i = 0; i < resultNum; i++)
{
int n = num>>>(byteNum*i)&(hexAfter-1);
if (n < 10)
sb.append(n);
else
sb.append((char)(n- 10 + 'A')); //可以考慮使用查表法進行對應轉換。
}
sb.reverse();
for(int i =0; i < sb.length(); i++) //求出第一個不爲0的角標 x
{
if(sb.charAt(i) != '0')
{
x = i;
break;
}
}
return sb.delete(0,x).toString();
}
//其他進制轉換爲十進制,算法不是按位運算,被轉換數始終認定爲正數
static String toDec(int hexFront, String numFront)
{
//前提: 輸入的數前面不是0開頭,被轉換數不能超出轉換數類型最大範圍。這裏爲int
int num = 0;
for(int i = 0; i < numFront.length(); i++)
{
char ch = numFront.charAt(numFront.length()-1-i);
if(ch >= 'A' && ch <= 'Z')
num += (ch -'A' + 10)*(Math.pow(hexFront,i)); //傻逼了,hexFront^0 是位異或運算,不是次方運算。
else
num +=(ch -'0')*(Math.pow(hexFront,i));
}
if(num == 2147483647 && !numFront.equals("01111111111111111111111111111111")&& !numFront.equals("17777777777")&& !numFront.equals("7FFFFFFF"))
return "被轉換數超出轉換數類型範圍,精度丟失!";
return String.valueOf(num);
}
}
8.小結:語言有的功能直接用,沒有的搞懂原理自己造。
開發用的:
1.排序:開發的時候直接用Arrays.sort(arr); ,在類java.util.*中,默認從小到大排序。其他語言不一定有,還是要自己實現
2.查找:開發的時候直接用Arrays.binarySearch(arr,57); ,在類java.util.*中。其他語言不一定有,還是要自己實現。
3.進制轉換:實際開發:用Integer.toBinaryString(-6)等等…