1:二进制中1的个数
第一种做法:最直接的将数对二作除法和余数操作。统计余数中1的个数
第二种做法:将该二进制数和1做与运算,然后再右移操作。
但是这里有一个问题就是右移运算符,将最右边的几位丢弃。并且容易陷入死循环,剑指offer第79页专门提出这个问题并给出了解释
如果数字是一个无符号数,则用0填充最左边的n位,如果数字是一个有符号的数字,则用数字的符号位填补最左边的n位。
第三种做法也是我认为最好的做法:将该整数与减去1之后得到的数做与运算。
int c = 10; System.out.println(Integer.toBinaryString(c));
2:给定一个数组,知道该数组中有一个数出现的次数超过整体的一半,找出这个数
排序数组的中位数一定是这个数,而如果完全排序,会耗费很多的时间。而针对快速排序,每一趟都能确定一个元素的最终位置。
首先快速排序的算法一定要掌握:下面附上代码:
//确定一趟排序元素的位置 public static int identityTheIndex (int[] A,int low,int high){ int key = A[low]; while(low<high){ while(low<high&&A[high]>=key){ high--; } A[low] = A[high]; while(low<high&&A[low]<=key){ low++; } A[high] = A[low]; } A[low]=key; return low; } 递归进行排序 public static void quictSort(int[] A,int low, int high){ if(low <high){ int index = identityTheIndex(A,low,high); quictSort(A,low,index-1); quictSort(A,index+1,high); } }
受到快速排序的启示,可以边排序边判断,代码如下:
public static int moreThanHalfNumber(int[] A,int length){ int mid = length >> 1; int high = length - 1; int low = 0; int index = identifyTheIndex(A,low,high); while(index!=mid){ if(index>mid){ high = index - 1; index = identifyTheIndex(A,low,high); } else{ low = index + 1; index =identifyTheIndex(A,low,high); } } int result = A[mid]; return result; }
第二种方法:考虑遍历数组的时候保存两个值:一个是数组中的一个数字,一个是次数,当遍历下一个数字的时候,如果下一个数字和之前保存的数字相同,则次数加1,如果下一个数字和之前保存的数字不同,则次数减1,如果次数为0,则需要保存下一个数字,并把次数设为1.由于要找的数字出现的次数比其他所有数字出现的次数之后还要多,那么要找的数字肯定是最后一次把次数设为1时对应的数字。代码如下:
public static int moreThanHalfNumber2(int[] A,int length){ int result = A[0]; int time = 1; for(int i = 1;i<length;i++){ if(time == 1){ result = A[i]; time = 1; } else if(result == A[i]){ time ++ ; } else{ time -- ; } } return result; }
3:最小(大)的k个数
这道题可以借助上题的思想主要确定第K个元素的位置,最直接的想法就是快速排序。
第二种方法特别适合处理海量数据,首先创建一个大小为K的数据容器来存储最小的K的数字,接着每次从输入的n个数中读入一个数,如果容器中已有的数字少于K个,则直接读入;如果已经有K个,也就是容器已满,这时只能替换。首先要找出这K 个数字中的最大的数,如果接下来的数字小于最大的数将替换,否则继续遍历下一个数
因此,容器满了需要做3件事:
1)K个整数中找出最大的数
2)有可能在这个容器中删除最大数
3)有可能要插入一个新的数字
如果用二叉树来实现这个数据容器,就能在O(logk)时间内完成,对于n个输入数字而言,总的时间效率就是O(nlogk)
很容易想到最大堆。
下面先复习一下建堆,以及堆排序的过程。
建堆:从下向上,建好的堆,根节点最大或最小。代码如下:
public static void createMaxdHeap(int[] data, int lastIndex) { for (int i = (lastIndex - 1) / 2; i >= 0; i--) { // 保存当前正在判断的节点 int k = i; // 若当前节点的子节点存在 while (2 * k + 1 <= lastIndex) { // biggerIndex总是记录较大节点的值,先赋值为当前判断节点的左子节点 int biggerIndex = 2 * k + 1; if (biggerIndex < lastIndex) { // 若右子节点存在,否则此时biggerIndex应该等于 lastIndex if (data[biggerIndex] < data[biggerIndex + 1]) { // 若右子节点值比左子节点值大,则biggerIndex记录的是右子节点的值 biggerIndex++; } } if (data[k] < data[biggerIndex]) { // 若当前节点值比子节点最大值小,则交换2者得值,交换后将biggerIndex值赋值给k swap(data, k, biggerIndex); k = biggerIndex; } else { break; } } } }
堆排序:将建好的堆根节点输出,然后将其与最后一个叶子节点交换,然后重新建堆,这个时候建堆的元素个数少一。
代码如下:
public static void heapSort(int[] data) { for (int i = 0; i < data.length; i++) { createMaxdHeap(data, data.length - 1 - i); swap(data, 0, data.length - 1 - i); print(data); } } public static void swap(int[] data, int i, int j) { if (i == j) { return; } data[i] = data[i] + data[j]; data[j] = data[i] - data[j]; data[i] = data[i] - data[j]; }
而这道题只是要求找出最小的K个数并没有要求有序,因此可以借助建堆的操作,代码如下:
public static void createMaxdHeap(int[] data, int lastIndex) { for (int i = (lastIndex - 1) / 2; i >= 0; i--) { // 保存当前正在判断的节点 int k = i; // 若当前节点的子节点存在 while (2 * k + 1 <= lastIndex) { // biggerIndex总是记录较大节点的值,先赋值为当前判断节点的左子节点 int biggerIndex = 2 * k + 1; if (biggerIndex < lastIndex) { // 若右子节点存在,否则此时biggerIndex应该等于 lastIndex if (data[biggerIndex] < data[biggerIndex + 1]) { // 若右子节点值比左子节点值大,则biggerIndex记录的是右子节点的值 biggerIndex++; } } if (data[k] < data[biggerIndex]) { // 若当前节点值比子节点最大值小,则交换2者得值,交换后将biggerIndex值赋值给k swap(data, k, biggerIndex); k = biggerIndex; } else { break; } } } } public static void print(int[] data) { for (int i = 0; i < data.length; i++) { System.out.print(data[i] + "\t"); } System.out.println(); } public static int[] getFirstNumber(int[] A, int k){ int[] B = new int[k]; for(int i =0;i<k;i++){ B[i] = A[i]; } heapSort(B); int count = k; while(count!=A.length){ /*int minmumValue = B[0]; int maxmumValue = B[k-1];*/ int maxmumValue = B[0]; //System.out.println("对排序后最小的元素:"+minmumValue); if(maxmumValue>A[count]){ int temp = A[count]; B[0] = temp; } System.out.println("替换元素之后的B:"); print(B); System.out.println("......................................"); heapSort(B); count++; } return B; } public static void main(String[] args) { int[] A = {80,55,45,7,8,6,3,10,2,1}; int k = 4; int[] C = getFirstNumber(A,k); System.out.print("最xiao的K个数为:"); for(int i = 0;i<k;i++){ System.out.print(C[i] + "\t"); } } 程序的输出结果为: 80 55 45 7 80 55 45 7 80 55 45 7 80 55 45 7 替换元素之后的B: 8 55 45 7 ...................................... 55 8 45 7 55 8 45 7 55 8 45 7 55 8 45 7 替换元素之后的B: 6 8 45 7 ...................................... 45 8 6 7 45 8 6 7 45 8 6 7 45 8 6 7 替换元素之后的B: 3 8 6 7 ...................................... 8 7 6 3 8 7 6 3 8 7 6 3 8 7 6 3 替换元素之后的B: 8 7 6 3 ...................................... 8 7 6 3 8 7 6 3 8 7 6 3 8 7 6 3 替换元素之后的B: 2 7 6 3 ...................................... 7 3 6 2 7 3 6 2 7 3 6 2 7 3 6 2 替换元素之后的B: 1 3 6 2 ...................................... 6 3 1 2 6 3 1 2 6 3 1 2 6 3 1 2 最xiao的K个数为:6 3 1 2