LintCode 228:链表的中点
https://www.lintcode.com/problem/middle-of-linked-list
描述
样例
输入: 1->2->3输出: 2样例解释: 返回中间节点的值输入: 1->2输出: 1样例解释: 如果长度是偶数,则返回中间偏左的节点的值。不重新遍历链表的情况下得到中点
解题思路
这题用快慢指针的思路来做。快指针每次移动两个位置,慢指针每次移动一个位置,当快指针移动到链表尾端时,慢指针就是中点。
AC代码
public ListNode middleNode(ListNode head) {// 链表为空直接返回nullif (head == null) {return null;}// 快慢指针,都从head开始ListNode fast = head;ListNode slow = head;while (fast.next != null) {// 快指针先走一步fast = fast.next;// 快指针还能走的话,快慢指针都走一步if (fast.next != null) {fast = fast.next;slow = slow.next;}}// 最后返回slow指针return slow;}
LintCode 539:移动零
https://www.lintcode.com/problem/move-zeroes
描述
给一个数组 nums 写一个函数将 0 移动到数组的最后面,非零元素保持原数组的顺序
1.必须在原数组上操作
2.最小化操作数
样例
输入: nums = [0, 1, 0, 3, 12],输出: [1, 3, 12, 0, 0].输入: nums = [0, 0, 0, 3, 1],输出: [3, 1, 0, 0, 0].
解题思路
一个思路是将前面出现的0与后面不是0的数字进行交换,但这样不能保证操作数最小。另一个思路是不用管0,因为0都会移到数组的最后面,所以只要我们把非零元素按原来的顺序挪动到数组的前面,后面设置为0就完成了0的移动。用一个index指针遍历数组,一个位置指针表示不为0的数现在应该移动到的位置,index指针数为0不管,不为0就将其移动至位置指针处,位置指针右移。
AC代码
public void moveZeroes(int[] nums) {// write your code here// 设置index指针和位置指针初始值,都从0开始int index = 0, notZero = 0;while (index < nums.length) {if (nums[index] != 0) {nums[notZero++] = nums[index];}index++;}while (notZero < nums.length) {nums[notZero++] = 0;}}
LintCode 608:两数和II-输入已排序的数组
https://www.lintcode.com/problem/two-sum-ii-input-array-is-sorted
描述
给定一个已经 按升序排列 的数组,找到两个数使他们加起来的和等于特定数。
函数应该返回这两个数的下标,index1必须小于index2。注意返回的值不是 0-based。
你可以假设每个输入刚好只有一个答案
样例
输入: nums = [2, 7, 11, 15], target = 9输出: [1, 2]输入: nums = [2,3], target = 5输出: [1, 2]
解题思路
因为题目保证只有一个答案,所以可以用上篇文字中左右指针的思路,left指针从小的数开始遍历,right指针从大的数开始遍历,两指针指向的和等于target则找到答案;若和大于target则表示两数过大,这时right指针左移(把数变小);若和小于target则表示两数过小,这时left指针右移(把数变大)。
AC代码
public int[] twoSum(int[] nums, int target) {// 设置左右指针int left = 0, right = nums.length - 1;int[] ans = new int[2];while (left < right) {int add = nums[left] + nums[right];// 和为target表示找到答案if (add == target) {ans[0] = left + 1;ans[1] = right + 1;break;// 和大于target,right指针左移} else if (add > target) {right--;// 和小于target,left指针右移} else {left++;}}return ans;}
LintCode 609:两数和-小于或等于目标值
https://www.lintcode.com/problem/two-sum-less-than-or-equal-to-target
描述
给定一个整数数组,找出这个数组中有多少对的和是小于或等于目标值。返回对数
样例
输入: nums = [2, 7, 11, 15], target = 24.输出: 5.解释:2 + 7 < 242 + 11 < 242 + 15 < 247 + 11 < 247 + 15 < 24输入: nums = [1], target = 1.输出: 0.
解题思路
上题的变种,思路一致,稍微处理一下即可。两数之和大于target时,right一直左移直至小于等于,然后此时left指针和right指针之间的数(包括right指针)之和都小于等于target,所以答案个数加上right-left,然后left指针右移。
AC代码
public int twoSum5(int[] nums, int target) {// 少于2个数直接返回0if (nums.length < 2) {return 0;}// 按升序排序Arrays.sort(nums);// 设置左右指针int left = 0, right = nums.length - 1;int ans = 0;while (left < right) {// 两数和大于target时,right左移直至小于等于targetwhile (left < right && nums[left] + nums[right] > target) {right--;}// 此时left指针和right指针之间的数(包括right指针)之和都小于等于targetif (left < right) {ans += right - left;}// left指针右移left++;}// 返回对数return ans;}
LintCode 533:两数和的最接近值
https://www.lintcode.com/problem/two-sum-closest-to-target
描述
给定整数数组num,从中找到两个数字使得他们和最接近target,返回两数和与 target 的差的 绝对值。
样例
输入: nums = [-1, 2, 1, -4] 并且 target = 4输出: 1解释:最小的差距是1,(4 - (2 + 1) = 1).输入: nums = [-1, -1, -1, -4] 并且 target = 4输出: 6解释:最小的差距是6,(4 - (- 1 - 1) = 6).
解题思路
还是之前两数和的变种题,用左右指针不断靠近target,更新最小差值即可。
AC代码
public int twoSumClosest(int[] nums, int target) {// 按升序排序Arrays.sort(nums);// 设置左右指针int left = 0, right = nums.length - 1;int ans = Integer.MAX_VALUE;while (left < right) {int add = nums[left] + nums[right];// 两数和为target时,直接返回0if (add == target) {return 0;}// 和小于target,left右移,不断逼近targetif (add < target) {left++;}// 和大于target,right左移,不断逼近targetelse {right--;}// 比较最小差值ans = Math.min(ans, Math.abs(add - target));}// 返回差值return ans;}
LintCode 443:两数之和 II
https://www.lintcode.com/problem/two-sum-greater-than-target
描述
给一组整数,问能找出多少对整数,他们的和大于一个给定的目标值
O(1)额外空间以及(nlogn)时间复杂度
样例
输入: [2, 7, 11, 15], target = 24输出: 1解释: 11 + 15 是唯一的一对输入: [1, 1, 1, 1], target = 1输出: 6
解题思路
AC代码
public int twoSum2(int[] nums, int target) {// 排序int ans = 0;Arrays.sort(nums);// 设置左右指针int left = 0, right = nums.length - 1;// 遍历数组while (left < right) {int add = nums[left] + nums[right];// 和小于target,left指针右移if (add <= target) {left++;} else {// 和大于targt,那么从left到right间的数(包括left)与right指针的和都大于target// right指针左移ans += right - left;right--;}}// 返回答案return ans;}
LintCode 461:无序数组K小元素
https://www.lintcode.com/problem/kth-smallest-numbers-in-unsorted-array
描述
找到一个无序数组中第K小的数
O(nlogn)的算法固然可行,但如果你能O(n)解决,那就非常棒了。
样例
输入: [3, 4, 1, 2, 5], k = 3输出: 3输入: [1, 1, 1], k = 2输出: 1
解题思路
快速选择的思路,跟快排中partition部分一样,随机选择数组中的一个数t,将小于t的数都放至左边,大于t的数放至右边,这样如果左边数的个数大于等于K,则K小元素在左半部分,递归查找左半部分;反之查找右半部分。
AC代码
public int kthSmallest(int k, int[] nums) {// 数组从0开始所以把k-1return partition(nums, 0, nums.length - 1, k - 1);}// 快速选择,和快排中的partition类似private int partition(int[] nums, int start, int end, int k) {if (start == end) {return nums[start];}// 从左右两端开始int left = start, right = end;int mid = nums[left + (right - left) / 2];while (left < right) {// 从左指针扫描找到比mid大的数while (left <= right && nums[left] < mid) {left++;}// 从右指针扫描找到比mid小的数while (left <= right && nums[right] > mid) {right--;}// 进行交换if (left <= right) {int tmp = nums[left];nums[left++] = nums[right];nums[right--] = tmp;}}// 进行partition操作后,left 要么等于 right + 1,要么等于right + 2// start到right的数小于 mid,left到end的数大于mid// 如果right >= k 则表示 左半部分的数至少有 k 个,所以在start 和 right 中查找答案if (right >= k && start <= right) {return partition(nums, start, right, k);}// 如果left <= k 则表示 左半部分的数少于 k 个,所以要去left 和 end 中查找答案if (left <= k && left <= end) {return partition(nums, left, end, k);}// 上述条件都不满足,则表明当前k的位置便是答案return nums[k];}
LintCode 382:三角形计数
https://www.lintcode.com/problem/triangle-count
描述
给定一个整数数组,在该数组中,寻找三个数,分别代表三角形三条边的长度,问,可以寻到多少组这样的三个数来组成三角形?
样例
输入: [3, 4, 6, 7]输出: 3解释:可以组成的是 (3, 4, 6),(3, 6, 7),(4, 6, 7)输入: [4, 4, 4, 4]输出: 4解释:任何三个数都可以构成三角形所以答案为 C(3, 4) = 4
解题思路
前面的443题是在数组中找出有多少组两个数的和大于一个给定的值,而三角形的三条边应满足的条件便是两边之和大于第三边,正好和该题要求的问题一致,所以可以将数组排序,然后从第三个数开始遍历,然后求该数前面的数中能找出多少组两数之和大于它。
AC代码
public int triangleCount(int[] S) {// 排序Arrays.sort(S);int ans = 0;// 从第三个数开始枚举for (int i = S.length - 1; i > 1 ; i--) {// 左指针从0开始,右指针从当前数的前一位开始int left = 0, right = i - 1;while (left < right) {// 两数之和大于答案加一,right指针左移if (S[left] + S[right] > S[i]) {ans += right - left;right--;} else {// 两数之和小于,left指针右移left++;}}}return ans;}
LintCode 144:交错正负数
https://www.lintcode.com/problem/interleaving-positive-and-negative-numbers
描述
给出一个含有正整数和负整数的数组,重新排列成一个正负数交错的数组。
不需要保持正整数或者负整数原来的顺序。完成题目,且不消耗额外的空间。
样例
输入 : [-1, -2, -3, 4, 5, 6]输出 : [-1, 5, -2, 4, -3, 6]解释 : 或者任何满足条件的答案
解题思路
一个简单的思路就是把负数集中起来,正数集中起来(可以用快速选择实现,也就是前面题目中的partition部分),然后交替拜访即可。(负数多先放负数,正数多先放正数)
AC代码
public void rerange(int[] A) {// 首先将小于0的数移动到左边,大于0的数移动到右边int left = 0, right = A.length - 1;while (left < right) {while (left < right && A[left] < 0) {left++;}while (left < right && A[right] > 0) {right--;}if (left < right) {int tmp = A[left];A[left++] = A[right];A[right--] = tmp;}}// 因为负数都在左边,正数都在右边,所以隔一个负数就和正数进行一次交换,就能满足正负排列// 负数从0开始,正数从最后开始int start = 0, end = A.length - 1;// 正数个数,left为负数个数int posCount = A.length - left;// 负数多,那从负数的第二个开始交换,最后队列是负正负正。。。。负正负。if (posCount < left) {start++;// 正数多,从负数的第一个和正数的第二个(从右往左)开始交换,最后队列是正负正负。。。。正负正。} else if (posCount > left) {end--;// 正负一样多,从负数的第二个和正数的第二个(从右往左)开始交换,最后队列是负正负正。。。。负正负正。} else {start++;end--;}// 交换一个跳一格继续交换,直到交换完成while (start < end) {int tmp = A[start];A[start] = A[end];A[end] = tmp;start += 2;end -= 2;}}
