题目描述:JZ50数组中重复的数字
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
function duplicate(numbers, duplication) {
var len=numbers.length;
if(numbers == null || len == 0){
return false;
}
numbers=numbers.sort();
for(var i=0;i<len-1;i++){
if(numbers[i] == numbers[i+1]){
duplication[0] = numbers[i];
return true;
}
}
return false;
}
function duplicate(numbers, duplication)
{ var len=numbers.length;
var map=new Map();
for(var i=0;i<len;i++){
if(map.has(numbers[i])){
duplication[0] = numbers[i];
return true;
}else{
map.set(numbers[i],1);
}
}
return false;
}
function duplicate(nums, duplication) {
var len=nums.length;
if(nums == null || length == 0){
return false;
}
for(var i=0;i<len;i++){
while(nums[i] != i){
if(nums[i] == nums[nums[i]]){
duplication[0] = nums[i];
return true;
}
// 交换nums[i]和nums[nums[i]]
var tmp = nums[i];
nums[i] = nums[tmp];
nums[tmp] = tmp;
}
}
return false;
}
另一个大佬的解法,觉得更简洁。
题目指出 在一个长度为 n 的数组 nums 里的所有数字都在 0 ~ n-1 的范围内 。 因此,可遍历数组并通过交换操作使元素的 索引 与 值 一一对应(即 nums[i] = i )。因而,就能通过索引找到对应的值。
遍历中,当第二次遇到数字 x 时,一定有 nums[x] = x (因为第一次遇到 x 时已经将其交换至 nums[x] 处了)。利用以上方法,即可得到一组重复数字。
算法流程
遍历数组 nums ,设索引初始值为 i = 0:
若 nums[i] == i : 说明此数字已在对应索引位置,无需交换,因此执行 i += 1 与 continue ;
若 nums[nums[i]] == nums[i] : 说明索引 nums[i] 处的元素值也为 nums[i],即找到一组相同值,返回此值 nums[i];
否则: 当前数字是第一次遇到,因此交换索引为 i 和 nums[i] 的元素值,将此数字交换至对应索引位置。
若遍历完毕尚未返回,则返回 -1 ,代表数组中无相同值。
参考链接
var findRepeatNumber = function(nums) {
var i = 0;
while(i < nums.length) {
if(nums[i] == i) {
i++;
continue;
}
if(nums[nums[i]] == nums[i])
return nums[i];
var tmp = nums[i];
nums[i] = nums[tmp];
nums[tmp] = tmp;
}
return -1;
}
题目:合并两个有序数组
思路1:先将两数组拼接,然后sort排序。缺点是没有利用nums1和nums2本身是有序数组的优势。
数组得拼接方法:
1.循环遍历:
for (var i = 0;i<nums2.length;i++) nums1[m+i] = nums2[i];
2.concat方法:
但这个题目不能使用concat拼接数组,因为concat方法会返回一个新的数组,不符合题意。题目中要求最终返回得是num1.
3.可使用splice方法,splice方法得操作会改变原数组,但其返回的值是删除项,我们不需要只要不对其返回值进行接收即可。
var merge = function(arr1, m, arr2, n) {
arr1.splice(m, n, ...arr2)
//splice方法会影响原数组,但其返回的值是删除项
arr1.sort((a, b) => a - b)
return arr1
}
思路二:双指针法–>从后向前数组遍历
因为 nums1 的空间都集中在后面,所以从后向前处理排序的数据会更好,节省空间,一边遍历一边将值填充进去
设置指针 len1 和 len2 分别指向 nums1 和 nums2 的有数字尾部,从尾部值开始比较遍历,同时设置指针 len 指向 nums1 的最末尾,每次遍历比较值大小之后,则进行填充
当 len1<0 时遍历结束,此时 nums2 中海油数据未拷贝完全,将其直接拷贝到 nums1 的前面,由于将较大得最后得到结果数组
时间复杂度:O(m+n)
参考大佬
var merge = function(nums1, m, nums2, n) {
let len1 = m - 1;
let len2 = n - 1;
let len = m + n - 1;
while(len1 >= 0 && len2 >= 0) {
// 注意--符号在后面,表示先进行计算再减1,这种缩写缩短了代码
nums1[len--] = nums1[len1] > nums2[len2] ? nums1[len1--] : nums2[len2--];
}
function arrayCopy(src, srcIndex, dest, destIndex, length) {
dest.splice(destIndex, length, ...src.slice(srcIndex, srcIndex + length));
}
// 表示将nums2数组从下标0位置开始,拷贝到nums1数组中,从下标0位置开始,长度为len2+1
arrayCopy(nums2, 0, nums1, 0, len2 + 1);
};
注意:如果len2先小于0就表示nums2已经循环完了,已经将nums2的所有元素都已经有序放入到nums1里面了
或者
只判断nums2,如果nums1数组遍历完成之后,index1=-1,就将nums2剩余的元素赋值给tail指针,直到nums2的长度为0.
const merge = (nums1, m, nums2, n) => {
let index1 = m - 1
let index2 = n - 1
let tail = m + n - 1
while (index2 >= 0) {
if (nums1[index1] > nums2[index2]) {
nums1[tail] = nums1[index1]
index1--
tail--
} else {
nums1[tail] = nums2[index2]
index2--
tail--
}
}
}
思路三:双指针你,从前往后,比较较小的元素。
请思路大概是这样,java版,没有去证实。
题目三:0-n-1中缺失的数字:
var missingNumber = function(nums) {
for(var i=0;i<=nums.length;i++){
if(nums.indexOf(i)===-1){
return i;
}
}
};
方法二:只要比较数组下标和该下标对应的值即可,但是如果输入的是[0]的话,nums[0]===0,没有返回值,完整的数组的长度为nums+1,而nums数组中每个数对应的索引为0到数组的长度-1,如果前nums.length位都是对应相等的,那么缺少的就是最后一位。(完整数组有10个数,每个数的索引范围为0-9,但是nums的长度为9,如果nums索引为0-8的位置都对应相等,那么就返回索引值为9,最后一个数)
看题目的意思就是完整的数组应该有
var missingNumber = function(nums) {
for (var i=0;i<nums.length;i++){
if (nums[i]!=i) return i;
}
return nums.length;
}
二分法:
var missingNumber = function(nums) {
var i = 0, j = nums.length - 1;
while(i <= j) {
var m = Math.floor((i + j) / 2);
if(nums[m] == m)
{
i = m + 1;
}
else
{
j = m - 1;
}
//console.log(i,j,m);
}
return i;
}