100题与剑指

剑指

10二进制中的1的个数

trick:减去1然后进行相与操作

11数值的整数次方

trick:注意边界值的点

13在O(1)时间删除链表节点

trick:注意当前结点是链表的最后一个结点

33把数组排成最小的数

trick:这里面要自己设计一个排序的比较器,自反性,对称性以及传递性

34丑数

trick:这块的指针指向了数组中的元素自身

36数组中的逆序对

trick:归并排序的思想

41和为s的两个数组以及和为s的连续正数序列

trick:迁移到连续的正数序列值也是不变的

43n个骰子的点数

trick:二维dp来做的

50树中两个节点的最低公共祖先

trick:先问是不是二叉搜索树,如果是的话可以依靠性质来做,如果是普通树,然后检查是否存在指向父节点的指针,转化为求链表的第一个交点,如果没有的话,转化为求链表的最后一个交点

61按照之字形顺序打印二叉树

trick:用的是stack

62序列化二叉树

trick:任何一种顺序都可以,

63二叉搜索树的第k个结点

trick:基本上利用的是中序遍历,因为java基础类型是值传递,复杂类型是引用传递,这个地方的标示第k要采用全局变量

开始扫下100题,体会下解题过程

二叉查找树转双链表

思路:递归,返回一个treenode数组,head tail

查找最小的k 个元素

思路:这个可以利用快排结合二分的思想,只针对其中需要的进行处理

    public static void getmin(int[] nums,int left,int right, int tar) {
        if (left >= right) return;
        int pa = getpartation(nums, left, right); 
        if (pa == tar - 1) return;
        else  if(pa >  tar - 1) getmin(nums, left, pa - 1 , tar);
        else getmin(nums, pa + 1, right, tar);
    } 
    public static int getpartation(int[]  nums, int left, int right) {
        int pivot = nums[left];
        int st = left;
        int end = right;
        while (st < end) {
            while (nums[end] > pivot && left < end) end--;
            while (nums[st] <= pivot && st < right) st++;
            swap(nums, st, end);
            st++;
            end--;
        }
        swap(nums, left, end);
        return end;
    }
    public static void swap(int[] nums, int x, int y) {
        int temp = nums[x];
        nums[x] = nums[y];
        nums[y] = temp;
    }

两个链表是否相交

思路:先检查是否有环,让长的先走,或者直接拼接

智力题:两个房间,3盏灯,3个开关

发热

链表反转的递归解法与非递归解法

递归时注意null, 与next.null

判断整数序列是不是二元查找树的后序遍历结果

思路:根据需求来判定, 先找到root结点

求二叉树中节点的最大距离…

思路:维护一个最大的,然后对每一个结点进行dfs

二叉树镜像递归版本与非递归版本

思路:非递归版本的采用stack,形同前序遍历

n 个数字(0,1,…,n-1)形成一个圆圈,从数字0 开始,循环删除第k个数字

约瑟夫问题
这是一个递推关系式,最后一个人编号假设为X,如果从n-1个人中的编号可以求出n个,那么便可以依次类推。 假设第m个人被杀 则 重新编号
m m+1 m+2 ……n-2 n-1 0 …… m-2 0 1 2 ……..n-2-m n-1-m n-1-m-2… n-2 X
假设幸存者在n-1队列中编号为X
那么可以知道在n编号中的X为 X+m, 举例 X=2,那么m+2为队列n中的编号 由于X+m可能会越界,所以(X+m)% n。

整数的二进制表示中1 的个数

google的一道面试题,这个利用1进行合理的判定,

有两个序列a,b,大小都为n,序列元素的值任意整数,无序;

要求:通过交换a,b 中的元素,使[序列a 元素的和]与[序列b 元素的和]之间的差最小。
感觉用分治法,递归减少,因为可以相互换,所以可以考虑

求一个有向连通图的割点,割点的定义是,如果除去此节点和与其相关的边,

有向图不再连通,描述算法。

一道关于飞机加油的问题,飞机满油跑一半,问至少多少架飞机,一个跑一圈

6架

5个海盗分100块宝石

首先,由1 号提出分配方案,然后大家表决,当且仅当超过半数的人同意时,
按照他的方案进行分配,否则将被扔进大海喂鲨鱼
如果1 号死后,再由2 号提出分配方案,然后剩下的4 人进行表决,
当且仅当超过半数的人同意时,按照他的方案进行分配,否则将被扔入大海喂鲨鱼。
依此类推
条件:每个海盗都是很聪明的人,都能很理智地做出判断,从而做出选择。
问题:第一个海盗提出怎样的分配方案才能使自己的收益最大化?
比较厉害的是 97, 0 , 1, 2,0, 逆向思考

9个点画出10条线

三行

在一天的24 小时之中,时钟的时针、分针和秒针完全重合在一起的时候有几次?都分别是什么时间?你怎样算出来的?

22次

四对括号可以有多少种匹配排列方式?比如两对括号可以有两种:()()和(())

卡特兰数问题,https://www.zhihu.com/question/25072237
凸多边形三角划分
在一个凸多边形中,通过若干条互不相交的对角线,把这个多边形划分成了若干个三角形。现在的任务是键盘上输入凸多边形的边数n,求不同划分的方案数f(n)。比如当n=6时,f(6)=14。
分析   
如果纯粹从f(4)=2,f(5)=5,f(6)=14,……,f(n)=n慢慢去归纳,恐怕很难找到问题的递推式,我们必须从一般情况出发去找规律。   
因为凸多边形的任意一条边必定属于某一个三角形,所以我们以某一条边为基准,以这条边的两个顶点为起点P1和终点Pn(P即Point),将该凸多边形的顶点依序标记为P1、P2、……、Pn,再在该凸多边形中找任意一个不属于这两个点的顶点Pk(2<=k<=n-1),来构成一个三角形,用这个三角形把一个凸多边形划分成两个凸多边形,其中一个凸多边形,是由P1,P2,……,Pk构成的凸k边形(顶点数即是边数),另一个凸多边形,是由Pk,Pk+1,……,Pn构成的凸n-k+1边形。   
此时,我们若把Pk视为确定一点,那么根据乘法原理,f(n)的问题就等价于:凸k多边形的划分方案数乘以凸n-k+1多边形的划分方案数,即选择Pk这个顶点的f(n)=f(k)×f(n-k+1)。而k可以选2到n-1,所以再根据加法原理,将k取不同值的划分方案相加,得到的总方案数为:f(n)=f(2)f(n-2+1)+f(3)f(n-3+1)+……+f(n-1)f(2)。看到此处,再看看卡特兰数的递推式,答案不言而喻,即为f(n)=h(n-2) (n=3,4,……)。
最后,令f(2)=1,f(3)=1。此处f(2)=1和f(3)=1的具体缘由须参考详尽的“卡特兰数”,也许可从凸四边形f(4)=f(2)f(3)+ f(3)f(2)=2×f(2)f(3)倒推,四边形的划分方案不用规律推导都可以知道是2,那么2×f(2)f(3)=2,则f(2)f(3)=1,又f(2)和f(3)若存在的话一定是整数,则f(2)=1,f(3)=1。(因为我没研究过卡特兰数的由来,此处仅作臆测)。   

最长公共字串。

题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二
给定两个字符串,看下最长的公共子串,子串可以不连续

找出数组中两个只出现一次的数字

比较厉害的一个解法,第一次异或确定两个数不同的位,第二次异或,根据第一次确定的分组,分别异或

利用递归实现栈的反转

//很有意思的一个题目
public static void reverse (Stack st) {
        if (st == null || st.size() == 0) return ;
        Object o = st.pop();
        reverse(st);
        putToButtom(st, o);
}
public static void putToButtom(Stack st , Object o) {
    if (st.size() == 0){
        st.push(o);
        return;
    }
    Object te = st.pop();
    putToButtom(st, o);
    st.push(te);
} 
  

n 个骰子的点数。把n 个骰子扔在地上,所有骰子朝上一面的点数之和为S。输入n,

打印出S 的所有可能的值出现的概率。
采用dp来解dp[6 * n + 1][n + 1],每次新加一个骰子

给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。

两边向中间进行排序操作

函数将字符串中的字符’‘移到串的前部分,前面的非’‘字符后移,但不能改变非’‘字符的先后顺序,函数返回串中字符’‘的数量。如原始串为:ab**cd**e*12,处理后为*****abcde12,函数并返回值为5。(要求使用尽量少的时间和辅助空间)

注意第二个while循环加条件

求两个串中的第一个最长子串。

如”abractyeyt”,”dgdsaeactyey”的最大子串为”actyet”。
利用后缀树来解

捣乱分子的个数

多人排成一个队列,我们认为从低到高是正确的序列,但是总有部分人不遵守秩序。如果说,前面的人比后面的人高(两人身高一样认为是合适的), 那么我们就认为这两个人是一对“捣乱分子”,比如说,现在存在一个序列:
176, 178, 180, 170, 171
这些捣乱分子对为
<176, 170>, <176, 171>, <178, 170>, <178, 171>, <180, 170>, <180, 171>,
那么,现在给出一个整型序列,请找出这些捣乱分子对的个数(仅给出捣乱分子对的数目即可,不用具体的对)
思路:先用一个数组index[],用来计算存储当前数的前面有几个大于它自身的,然后输出交换的数即可

求随机数构成的数组中找到长度大于=3 的最长的等差数列, 输出等差数列由小到大:

如果没有符合条件的就输出
格式:
输入[1,3,0,5,-1,6]
输出[-1,1,3,5]
要求时间复杂度,空间复杂度尽量小
先排序,时间复杂度为O(nlog N),用map对值和下标进行缓存, 用dp,其中dp[len][k], len为数组的下标,k为等差数列的差值,每次用map进行缓存

用C 语言实现函数void * memmove(void *dest, const void *src, size_t n)。memmove 函数的功能是拷贝src 所指的内存内容前n 个字节到dest 所指的地址上。

//比较优雅的代码
分析:
由于可以把任何类型的指针赋给void 类型的指针, 这个函数主要是实现各种数据类型的拷贝。
ANSWER
//To my memory, usually memcpy doesn’t check overlap, memmove do
void * memmove(void * dest, const void * src, size_t n) {
  if (dest==NULL || src == NULL) error(“NULL pointers”);
  byte * psrc = (byte*)src;
  byte * pdest = (byte*)dest;
  int step = 1;
  if (dest < src + n) {
    psrc = (byte*)(src+n-1);
    pdest = (byte*)(dest+n-1);
    step = -1;
  }
  for (int i=0; i

处理大数据相关的东西:

针对时间,我们可以采用巧妙的算法搭配合适的数据结构,如Bloom filter/Hash/bit-map/堆/数据库或倒排索引/trie树,针对空间,无非就一个办法:大而化小,分而治之(hash映射),你不是说规模太大嘛,那简单啊,就把规模大化为规模小的,各个击破不就完了嘛。

分而治之/hash映射 + hash统计 + 堆/快速/归并排序;
双层桶划分
Bloom filter/Bitmap;
Trie树/数据库/倒排索引;
外排序;
分布式处理之Hadoop/Mapreduce。

1、海量日志数据,提取出某日访问百度次数最多的那个IP。

既然是海量数据处理,那么可想而知,给我们的数据那就一定是海量的。针对这个数据的海量,我们如何着手呢?对的,无非就是分而治之/hash映射 + hash统计 + 堆/快速/归并排序,说白了,就是先映射,而后统计,最后排序:

分而治之/hash映射:针对数据太大,内存受限,只能是:把大文件化成(取模映射)小文件,即16字方针:大而化小,各个击破,缩小规模,逐个解决
hash_map统计:当大文件转化了小文件,那么我们便可以采用常规的hash_map(ip,value)来进行频率统计。
堆/快速排序:统计完了之后,便进行排序(可采取堆排序),得到次数最多的IP。
hash对1000取模,对小文件进行词频统计
由上面第1题,我们知道,数据大则划为小的,如如一亿个Ip求Top 10,可先%1000将ip分到1000个小文件中去,并保证一种ip只出现在一个文件中,再对每个小文件中的ip进行hashmap计数统计并按数量排序,最后归并或者最小堆依次处理每个小文件的top10以得到最后的结。

6、 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?

可以估计每个文件安的大小为5G×64=320G,远远大于内存限制的4G。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。

分而治之/hash映射:遍历文件a,对每个url求取,然后根据所取得的值将url分别存储到1000个小文件(记为,这里漏写个了a1)中。这样每个小文件的大约为300M。遍历文件b,采取和a相同的方式将url分别存储到1000小文件中(记为)。这样处理后,所有可能相同的url都在对应的小文件()中,不对应的小文件不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。
hash_set统计:求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。
OK,此第一种方法:分而治之/hash映射 + hash统计 + 堆/快速/归并排序,再看最后4道题,如下:

密匙二、多层划分

多层划分—-其实本质上还是分而治之的思想,重在“分”的技巧上!
  适用范围:第k大,中位数,不重复或重复的数字
  基本原理及要点:因为元素范围很大,不能利用直接寻址表,所以通过多次划分,逐步确定范围,然后最后在一个可以接受的范围内进行。
问题实例:
13、2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。
有点像鸽巢原理,整数个数为2^32,也就是,我们可以将这2^32个数,划分为2^8个区域(比如用单个文件代表一个区域),然后将数据分离到不同的区域,然后不同的区域在利用bitmap就可以直接解决了。也就是说只要有足够的磁盘空间,就可以很方便的解决。

1G 等于 10亿个数

密匙三:Bloom filter/Bitmap

Bloom filter
“13、在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数。

方案1:采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行,共需内存2^32 * 2 bit=1 GB内存,还可以接受。然后扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。
方案2:也可采用与第1题类似的方法,进行划分小文件的方法。然后在小文件中找出不重复的整数,并排序。然后再进行归并,注意去除重复的元素。”

密匙四、Trie树/数据库/倒排索引

密匙五、外排序

  适用范围:大数据的排序,去重
  基本原理及要点:外排序的归并方法,置换选择败者树原理,最优归并树

15. 最大间隙问题

给定n个实数,求着n个实数在实轴上向量2个数之间的最大差值,要求线性的时间算法。
用的是桶排序。

你可能感兴趣的:(算法小结)