逛博客时看到的,挺不错的,转来了。原作者:从零到无穷大
实现一个算法,找出单项链表中倒数第K个结点。
一种比较高效的解法是使用两个指针,使这两个指针分别指向链表中相距K个结点的两个结点。然后沿着链表同时移动这两个指针,当其中一个首先到达链表末端的时候,另一个指针指向的就是倒数第K个结点。显然,该算法的时间复杂度为O(n)。
public ListNode kthToLast(ListNode head, int k) {
if (K <= 0) return null;
ListNode fast = head;
ListNode slow = head;
// fast指针向前移动k个结点
for (int i = 1; i <= k; i++) {
if (slow == null) return null;
fast = fast.next;
}
if (fast == null) return null;
// fast到达末尾时,slow正好指向倒数第K个结点
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
检测链表是否存在环路。
使用快慢指针法,快指针一次移动2步,慢指针一次移动1步。如果链表中存在环路,则两个指针必然会相遇。使用这种快慢指针的方式可以为解决很多链表相关的问题提供思路,比如使用两倍速的快指针可以让慢指针定位到链表中间位置,三倍速的快指针可以让慢指针定位到链表三分之一的位置等等,这也是解决不少链表问题的基础。
代码(n & (n-1)) == 0是什么含义?
该代码可以用用来检查n是否为0或2的某次方。同样的n = n & (n-1)会清除n的最低有效位。
实现一个方法,不使用临时变量,直接交换两个数。
方法1:
public static void swap(int a, int b) {
a = a - b;
b = a + b;
a = b - a;
}
方法2:
public static void swap(int a, int b) {
a = a^b;
b = a^b;
a = a^b;
}
当转化为二进制时,只要能正确交换两个比特位即可。
实现一个方法,将两个数字相加,不能使用“+”号或其他运算符。
求解该题的关键在于把“相加”和“进位”两个操作分开进行,对于二进制,两个数相加而不考虑进位,即相当于进行异或操作。但是若只考虑进位,则又相当于按位与加上移位操作。因此代码可以实现为:
public static int add(int a, int b) {
if (b == 0) return a;
// 相加但不进位
int sum = a ^ b;
// 只进位
int carry = (a & b) << 1;
// 递归执行
return add(sum, carry);
}
有20瓶药丸,其中19瓶装有1克/粒的药丸,余下一瓶装有1.1克/粒的药丸。给你一台精准的天平,怎么找出比较重的那瓶药丸?天平只能用一次。
对20瓶药丸从1到20编号,从每瓶药丸中取出对应编号颗药丸,如从一号瓶中取出1颗,二号瓶中取出2颗……,然后把这些取出的药丸一起放到天平上称重。如果每颗药丸都重1克,那么总重量应为(1+2+3+4+……+19+20)= 210克,所以如果实际重量大于210克,那么多出来的重量一定来自于没粒多0.1克的那瓶药丸。所以由多出来的重量,除以0.1克,得到的即为瓶子的编号。
给定两条绳子,每条绳子烧尽正好需要一个小时,怎样用这两条绳子准确计量十五分钟?绳子密度不均匀。
此题关键在于,绳子密度虽然不均匀,但是如果从两头同时点燃绳子,那么一根绳子烧完正好需要30分钟。所以,把这两根绳子中的一根从一头点燃,另一根同时从两头点燃,等两头点燃的绳子烧完时,正好过去30分钟。这时从一头点燃的绳子还可以再烧30分钟,这时再点燃这根绳子的另一头,并开始计时,等它烧完时,正好计量15分钟。
给定一个方法isSubString()可以用来检查一个字符串是否是其他字符串的子串,再给定两个字符串s1和s2。实现一个方法,检查s2是否为s1旋转而成,且只能调用一次isSubString()方法。
举个例子:s2=colinwang就是由s1=wangcolin旋转而成,所以可以将s1分成两个部分,即:s1=xy,x=wang,y=colin。这时我们会发现yx一定会是xyxy的子串,也就是说,s2一定是s1s1的子串,所以只需要调用isSubString(s1s1,s2)即可。
给定一个能产生0到4之间整数随机数的方法rand5(),实现一个能产生0到6之间整数随机数的方法rand7()。
rand7()应当返回0到6之间的整数,且返回每个整数的概率都应为七分之一。我们可以使用while循环,产生出一个范围的数值(至少含有7个元素),其中每个数值出现的概率相同。这样,我们再舍弃掉其中大于7的倍数的部分,最后在除以7取余数,得到范围0到6的随机数,每个值出现的概率都是七分之一。见下面的代码:
public static int rand7() {
while(true) {
// 该rand取值在0到24之间,取得其中每个值的概率相同,
// 舍弃掉21、22、23、24,否则该方法返回的0到3之间的数字会偏多
int rand = 5*rand5() + rand5();
if (rand < 21) {
return rand % 7;
}
}
}
仍然有个问题要提一下,上面的代码中为什么选择rand=5*rand5()+rand5()呢,因为它能均匀的产生0到24之间的数字,这样就保证了0到24之间每个数字出现的概率相同。如果选择rand=2*rand5()+rand5()则不行,因为这些值不是均匀分布的,比如取得6有两种方式(6=2*1+4和6=2*2+2),而取得0却只有一种方式(0=2*0+0),这样就导致了每个值出现的概率不等。该题目的关键就在于找到一个范围,并使得其中每个值出现的概率相同。