对数组操作最基本的动作就是 : 存,取
核心思想:对角标的操作
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)等等…