Happiness is more than pleasure without pain

你只有非常努力,才能看起来毫不费力

导航

编程之美题选

1、求二进制数中1的个数:
1、除以2 根据余数 判断,迭代
2、与1相与,根据结果判断,循环移位
3、列出全部数据映射2-1 3-2 4-1 。。。 建立Hash表O(1)

4、(1100&1011=1000 能去掉最右边的1)
   intNumber(int n ){
      int count=0;
      while(n){
      count++;
      n=(n-1)&n;
    }
  }


2、求出数组中个数超过一半的数字A
每次删除2个不同的数字,那么在剩下的数字中,A仍会超过总数的一半。

int find(int ID[], int n){
    int nTimes = 0, i, candidate;
    for(i = 0; i < n;i++){
        if(nTimes == 0){
            candidate = ID[i];
            nTimes = 1;
        }
        else{
            if(candidate == ID[i])
                nTimes++;
            else
                nTimes--;
        }
    }
    return candidate;
}

现在有3个发帖水王,发帖数目都超过了总数的1/4,怎么找出这三个人?
现在我们需要3个变量来记录当前遍历过的3个不同的ID,而nTimes的3个元素分别对应当前遍历过的3个ID出现的个数。如果遍历中有某个ID不同
于这3个当前ID,我们就判断当前3个ID是否有某个的nTimes为0,如果有,那这个新遍历的ID就取而代之,并赋1为它的遍历数(即nTimes减1)
,如果当前3个ID的nTimes皆不为0,则3个ID的nTimes皆减去1。

3、n个数字中取出前K大的数;
方法一:
1、用快排的思想,a[0]为基准,划分数组为Sa都大于a[0],Sb都小于a[0]
2、若|Sa|<K,则Sa中的所有数加上Sb中的K-|Sa|个数就是数组的前K大
3、若|Sa|>K,则Sa中的前K个数就是数组的前K大
4、这样递归下去,不断把问题分解为更小的子问题,平均复杂度为O(N*logK)
方法二:
用最小堆时间复杂度也是O(N*logK)
空间复杂度是O(K)
在N非常大,数据需要存储在磁盘上,而K相对很小,可以在内存上轻易维护大小为K的堆的情况下,在减少磁盘I/O上会有一定的优势,因为每个
元素只需要被读取一次。

4、得到数组的最大值和最小值:
方法一:每2个数字比较,大的放在偶数位,小的在奇数位,在各自求极值,1.5N的复杂度
方法二:每2个数字比较,大的再和MAX比较,小的再和MIN比较,不用破坏数组,也是1.5N。
方法三:也可以用分治,求前后N/2的MIN和MAX,小小比较得到MIN,大大比较得到MAX,递归

5、求二维平面上N个点,距离最小的2点
方法一:穷举O(n^2)
方法二:分治法,中点x对半分成2个区域,分别求最小得出min,但可能出现左右2边有2点间距最小,只考虑在[x-min,x+min]内的点,以此解法
递归求解

数组中取出2个数的和等于给定值A
方法一:O(n^2) a[i]+a[j]==A? 或者用a[j]==A-a[i]?
方法二:消耗O(n)的空间存储Hash表,根据映射查找A-a[i]是否在表中
方法三:先排序O(nlogn),再用双指针left(0), right(n-1)指向2端,若之和大于A则right移动
否则left移动。


6、N-1子数组的最大乘积
不急分情况讨论:(N个数的乘积)
1、p=0 再分情况:q为N-1个数的乘积
q=0解为0
q<0解为0
q>0为解
2、p<0去掉绝对值最小的那个负数即可
3、p>0去掉绝对值最小的那个正数
(+暴力破解)O(n^2)

7、求子数组最大和:
方法一:暴力O(n^3)
方法二:分治法:
A[n]最大字段和值为三种情况:
1、A[n/2-1]的最大字段和
2、A[n/2]到A[n]的最大字段和
3、最大字段和横跨A[n/2-1]和A[n/2](卡中间,计算Sum=sum1+sum2)

方法三:动态规划法
//如果sum>Max 则更新Max
//如果sum<0 则 更新sum为0 继续循环
//否则是0<sum<Max 说明sum是有潜力的,后来可能会更新为更大的值

for(int i=1;i<=t;i++){
sum+=a[i];//sum存储当前最大的b[j], b存储b[j]
if(sum>Max)Max=sum;
if(sum<0)sum=0;
}
//Max存储全局最大,sum随着指针移动而变化,为负数则置0
//找到更大的则赋给Max,Max始终存储全局最大

8、求数组中最长递增子序列:
前i个元素中最长递增子序列为LIS[i]
则有:LIS[i+1]=max{1,LIS[k]+1}

for(int i=0;i<arr.length;i++){
	LIS[i]=1;
	for(int j=0;j<i;j++){
		if(arr[i]>arr[j]&&LIS[j]+1>LIS[i])
			LIS[i]=LIS[j]+1;
	}
}
return Max(LIS); 

9、数组循环右移K位,复杂度O(n),最多允许使用2个变量
abcd1234右移4位->1234abcd
前4位逆序排列 abcd1234->dcba1234
后4位逆序排列dcba1234->dcba4321
全部逆序dcba4321->1234abcd

Reverse(int* arr,int b,int e){
	for(;b<e;b++,e--){
		int temp=arr[e];
		arr[e]=arr[b];
		arr[b]=temp;
	}
}
RightShift(int* arr,int N,int k){
	K%=N;
	Reverse(arr,0,N-K-1);
	Reverse(arr,N-K,N-1);
	Reverse(arr,0,N-1);
}

(没要求空间复杂度才可以创建一个新的数组直接O(N)copy过去)
一位一位移动的复杂度是O(K*N)

10、数组分割:使得2份之和 尽量接近


11、字符串移位包含:
1、暴力破解
2、判断s2是否是s1的移位包含,可以将2个s1连起来以避免移位。
AABCD AABCD包CDAA 空间换时间

12、从无头单链表中删除节点:
假设一个没有头指针的单链表。一个指针指向此单链表中间的一个节点p(不头不尾),要删除该节点p
"狸猫换太子":可以把p节点后面的节点q的value赋值到p节点,
然后删除节点q即可
q=p-next;
p->data=q->data;
p-next=q-next;

13、判断2个链表a,b是否相交:
1、暴力解法 依次判断a中节点是否在b中,复杂度O(length(a)*length(b))
2、用hash建立其中一个链表的节点地址查询hash表
3、可以将b链表表头连接在a表的后面,在判断链表是否有环
4、直接判断最后一个节点是否是同一节点,复杂度O(length(a)+length(b))是最快捷的解法

(怎样找到第一个公共结点?)链表长的-短的=i  长的先走i步,然后一起走,每步判断结点是否相同

14、重建二叉树:根据前序和中序


15、分层遍历二叉树:

posted on 2015-03-22 11:11  believer  阅读(240)  评论(0编辑  收藏  举报