[ 算法题 ] 冒泡排序 && 两数之和的5种实现方式

285 阅读3分钟

目录

  1. 冒泡排序
  2. 两数之和 ([],Number) => []

一 冒泡排序

比较两个记录键值的大小,如果这两个记录键值的大小出现逆序,则交换这两个记录

function bubbleSort(arr){
    for(let i = 1;i < arr.length;i++){
        for(let j = i;j > 0;j--){
            if(arr[j] < arr[j-1]){
                [arr[j],arr[j-1]] = [arr[j-1],arr[j]];
            }
        }
    }
    return arr;
}
var arr = [3,4,2,1];
bubbleSort(arr); // [1, 2, 3, 4]

二 两数之和 ([],Number) => []

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值那两个 整数,并返回它们的数组下标

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

可以遍历所有数字对,双重循环遍历当前值和另一个当前值与目标值是否相等,如果相等返回结果。

1) 遍历方式 ([],Number) => []

从第0个开始,依次和后面的数相加判断,不满足条件后, 再从第1个开始,依次和后面的数相加判断,直到,最后一个数. 优化: (可以少一次遍历) 外层for遍历条件 可以修改为: i < nums.length-1

/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
 for (let i = 0; i < nums.length-1; i++) { // 优化: (可以少一次遍历) `外层for遍历条件` 可以修改为: `i < nums.length-1`
  console.log('i:',i)
  for (let j = i + 1; j < nums.length; j++) {
   console.log('j:',j);
   if (nums[i] + nums[j] === target) {
    return [i,j];
   }
  }
 }
};
var nums = [200,70,2]; 
var target = 290;
twoSum(nums, target);

image.png

执行过程:

i: 0  j: 1 2
i: 1  j: 2
i: 2  j: 不执行

  • 第0个数和第二个,第三个相加,满足条件return
  • 第1个数和第二个数相加,满足条件return
  • 第2个数是最后一个数了,内层循环结束
var nums = [200,70,2]; 
var target = 270;
twoSum(nums, target);

image.png

2) 遍历方式 + indexOf

var twoSum = function(nums, target) {
 for(let i = 0; i < nums.length; i++) {
  var index = nums.indexOf(target-nums[i]);
  if(index !== -1 && index !== i) {
   return [i,index]
  }
 }
};
var nums = [2,3,4]; 
var target = 7;
twoSum(nums, target); // [1, 2]

3) 遍历方式 借助Map存储

var twoSum = function(nums, target) {
  const map = new Map();
  for (let i = 0; i < nums.length; i++) {
   const diff = target - nums[i];
   if (map.has(diff)) {
    return [map.get(diff), i];
   }
   map.set(nums[i], i);
  }
};
var nums = [2,3,4]; 
var target = 7;
twoSum(nums, target); // [1, 2]

4) 双指针方式

用两个指针进行查找,提高查找的效率

var twoSum = function(nums, target) {
    if(!nums.length) return [];
    let num = nums.slice(0);
    nums.sort((x,y) => x-y);
    let l = 0,r = nums.length-1;
    while(l < r){
        if(nums[l] + nums[r] === target) break;
        else if(nums[l] + nums[r] < target){
            l++;
        }else{
            r--;
        }
    }
    l = num.indexOf(nums[l]);
    r = num.indexOf(nums[r]) === l ? num.indexOf(nums[r],l+1) : num.indexOf(nums[r])
    return [l,r];
};
var nums = [2,3,4]; 
var target = 7;
twoSum(nums, target);

5) 查找表法

  • 在遍历的同时,记录一些信息,以省去一层循环,这是以空间换时间的想法

  • 需要记录已经遍历过的数值和他对应的下标,可以借助查找表来实现

  • 查找表常用的两个实现

    • 哈希表
    • 平衡二叉树搜索
function twoSum(arr, target) {
    var temp = []
    let len = arr.length
    for (let i = 0; i < len; i++) {
        let dif = target - arr[i]
        if (temp[dif] !== undefined) {
            return [temp[dif], i]
        }
        temp[arr[i]] = i
    }
};
let arr = [2, 3, 5, 9]
console.log(twoSum(arr, 7)) // [0, 2]

  • 通过 let dif = target - arr[i] 分别获取目标值当前遍历元素的差值,如果这个差值作为哈希表的索引,能访问到有效元素,那么就说明当前遍历的itemp[dif] ,就是要找的索引

[2, 3, 5, 9] 目标值是 7

  1. i = 0; dif = 7 - 2 = 5 此时temp[dif] = temp[5] = undefined

所以走 temp[arr[i]] = i; temp[2] = 0 // 哈希表存存储遍历过的数值和对应下标

  1. i = 1; dif = 7 - 3 = 4 此时 temp[dif] = temp[4] = undefined

所以走 temp[arr[i]] = i; temp[3] = 1 // 哈希表存存储遍历过的数值和对应下标

  1. i = 2; dif = 7 - 5 = 2 此时 temp[dif] = temp[2] = 0

此时这个差值作为哈希表的索引,能访问到有效元素,那么就说明当前遍历的itemp[dif] ,就是要找的索引 , 所以return [temp[dif], i] 所以 最后结果是[0, 2]

参考

总结

  • 双数之后实现思路: 双层遍历 | 单层遍历(并借助对象存储) | 双指针