三道题 2015.8.26(二进制中1的个数,数组中出现次数超过一半的数字,最大(小)的K个数)

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] + "	");  
        }  
        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] + "	"); 
		}
}

程序的输出结果为:
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	

  

原文地址:https://www.cnblogs.com/gracyandjohn/p/4761701.html