JavaScript实现全排列

文章目录

  • 一、全排列
    • 1. 思路
    • 2. 代码实现
      • 2.1 递归实现
      • 2.2 结果展示
  • 二、排序
    • 1. 代码实现
    • 2. 进阶实现
  • 三、两数之和
    • 1. 思路
    • 2. 代码实现
  • 四、寻找第K大
    • 1. 思路
    • 2. 代码实现
  • 五、 最小的K个数
    • 1. 思路
    • 2. 代码实现
  • 六、instanceof
    • 1. 思路
    • 2. 代码实现

一、全排列

       从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。简单的来说:给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: _permute(‘abc’)
输出: [ ‘abc’, ‘acb’, ‘bac’, ‘bca’, ‘cab’, ‘cba’ ]

1. 思路

JavaScript实现全排列_第1张图片
       通过图片可以得到,全排列的代码可以从两个维度进行分析:for循环横向遍历,递归纵向遍历。for循环控制首位元素的获取,递归控制后续元素的获取,此处需要注意的是需要进行当前元素是否已经被获取,同时使用一个数组进行数据的存储,当且仅当该数组的长度等于初始数组的长度时,代表已找到一组结果,此时结束递归。

// 当获取的元素个数等于传入数组长度时(此时说明找到了一组结果)
                if (path.length === k) {
                    // 将这组数据存进结果集数组
                    res.push(path);
                    // 结束递归
                    return;
                }

       那么如何进行当前元素是否已被使用的判断呢?可以使用一个used数组,其实就是记录此时path里都有哪些元素使用了,一个排列里一个元素只能使用一次

2. 代码实现

2.1 递归实现

const _permute = string => {
            // 结果集数组
            const res = [];
            // 元素获取数组
            let path = '';
            // 调用backTracking函数
            // 参数1:需要全排的数组 参数2:数组的长度 参数3:used数组记录当前元素是否已被使用
            backTracking(Array.from(string), string.length, []);
            // 返回最后的结果
            return res;

            // 递归函数
            function backTracking(n, k, used) {
                // 当获取的元素个数等于传入数组长度时(此时说明找到了一组结果)
                if (path.length === k) {
                    // 将这组数据存进结果集数组
                    res.push(path);
                    // 结束递归
                    return;
                }
                for (let i = 0; i < k; i++) {
                    if (used[i]) continue; // 当前元素已被使用, 结束此次循环
                    path = path + n[i]; // 将符合条件的元素存进path数组
                    used[i] = true; // 并将该元素标为true,表示已使用同支
                    backtracking(n, k, used);
                    path = path.substring(0, path.length - 1)
                    used[i] = false;
                }
            }
        };
console.log(_permute('abc'));

2.2 结果展示

JavaScript实现全排列_第2张图片

二、排序

给定一个长度为 n 的数组,请你编写一个函数,返回该数组按升序排序后的结果。

要求:时间复杂度 O(n^2),空间复杂度 O(n)
进阶:时间复杂度 O(nlogn),空间复杂度 O(n)
注:本题数据范围允许绝大部分排序算法,请尝试多种排序算法的实现。

1. 代码实现

       使用冒泡排序法实现,时间复杂度 O(n^2),空间复杂度 O(n)。

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 * 将给定数组排序
 * @param arr int整型一维数组 待排序的数组
 * @return int整型一维数组
 */
function MySort(arr) {
    // write code here
    for (let i = 0; i < arr.length - 1; i++) {
        for (let j = i + 1; j < arr.length; j++) {
            if(arr[j] < arr[i]) {
                let temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }
    return arr;
}

module.exports = {
    MySort: MySort,
};

2. 进阶实现

       使用快速排序法实现,时间复杂度 O(nlogn),空间复杂度 O(n)。

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 * 将给定数组排序
 * @param arr int整型一维数组 待排序的数组
 * @return int整型一维数组
 */
function MySort(arr) {
    // write code here
    QuickSort(arr, 0, arr.length - 1);
    return arr;
}

// 快速排序设计
function QuickSort(arr, s, t) {
    let left = s + 1,
        right = t,
        x = arr[s];

    while (left <= right) {
        while (left <= right && arr[left] <= x) left++;
        while (left <= right && arr[right] >= x) right--;

        if (left < right) {
            let temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
            left++;
            right--;
        }
    }

    if (s != right) {
        arr[s] = arr[right];
        arr[right] = x;
    }

    // 不同情况下的递归调用
    if (s < right - 1) QuickSort(arr, s, right - 1);
    if (right + 1 < t) QuickSort(arr, right + 1, t);
}
module.exports = {
    MySort: MySort,
};

三、两数之和

给出一个整型数组 numbers 和一个目标值 target,请在数组中找出两个加起来等于目标值的数的下标,返回的下标按升序排列。
(注:返回的数组下标从1开始算起,保证target一定可以由数组里面2个数字相加得到)
要求:空间复杂度 O(n),时间复杂度 O(nlogn)

示例
输入:[3,2,4],6
返回值:[2,3]
说明:因为 2+4=6 ,而 2的下标为2 , 4的下标为3 ,又因为 下标2 < 下标3 ,所以返回[2,3]

1. 思路

       最开始想到的算法是使用一个for循环控制数组使用includes()方法判断是否存在target - numbers[i]的值,若存在则使用break结束循环,反之则继续寻找。不出意外的超时了。

/**
 *
 * @param numbers int整型一维数组
 * @param target int整型
 * @return int整型一维数组
 */
function twoSum(numbers, target) {
    // write code here
    let result = [];
    for (let i = 0; i < numbers.length; i++) {
        // 先得到目标变量和当前数组变量的差值
        let num = target - numbers[i];
        // 使用includes进行差值是否存在于数组判断
        if (numbers.includes(num)) {
            let index = numbers.indexOf(num);
            if (index != i) {
                result.push(i + 1);
                result.push(index + 1);
                break;
            } else {
                continue;
            }
        }
    }
    return swap(result);
}

function swap(arr) {
    if (arr[0] > arr[1]) {
        let temp = arr[0];
        arr[0] = arr[1];
        arr[1] = temp;
    }
    return arr;
}
module.exports = {
    twoSum: twoSum,
};

       通过对上传代码的阅读,发现使用Map.has()哈希判断可以减少时间复杂度。

2. 代码实现

/**
 *
 * @param numbers int整型一维数组
 * @param target int整型
 * @return int整型一维数组
 */
function twoSum(numbers, target) {
    // write code here
    const res = new Array(2);
    let map = new Map(),
        len = numbers.length;
    for (let i = 0; i < len; i++) {
        if (map.has(target - numbers[i])) {
            res[0] = map.get(target - numbers[i]) + 1;
            res[1] = i + 1;
            break;
        } else {
            map.set(numbers[i], i);
        }
    }

    return res;
}

module.exports = {
    twoSum: twoSum,
};

四、寻找第K大

有一个整数数组,请你根据快速排序的思路,找出数组中第 k 大的数。
给定一个整数数组 a ,同时给定它的大小n和要找的 k ,请返回第 k 大的数(包括重复的元素,不用去重),保证答案存在。
要求:时间复杂度 O(nlogn),空间复杂度 O(1)。

示例
输入:
[1,3,5,2,2],5,3
返回值:2

1. 思路

      由题目我们可以根据快速排序的思路(以升序为例),其原理是先选取一个基准数据x,初始化左left右right指针变量,若left指针指向的数据小于等于基准数据x,则left向右移动,当指针指向的数据大于x时,left停止移动。相同的,right指针就是进行大于等于x的判断,同时left<=right是外层循环的执行条件。

	while(left <= right) {
        while(left <= right && arr[left] >= x) left++;
        while(left <= right && arr[right] <= x) right--;

        if(left < right) {
            let t = arr[left];
            arr[left] = arr[right];
            arr[right] = t;
        }
	}

同时使用right与k进行结果返回的判断,当right === k - 1时即找到第K大的数据,当right > k - 1时到right指针指向数据的左边寻找,right < k - 1时到right指针指向数据的右边去找。以此重复循环直到结果出现。

	if(right == k -1) {
        return arr[right];
    } else if(right > k - 1) {
        return QuickSort(arr, s, right - 1, k);
    } else {
        return QuickSort(arr, right + 1, t, k);
    }

2. 代码实现


/**
 * 
 * @param a int整型一维数组 
 * @param n int整型 
 * @param K int整型 
 * @return int整型
 */
function findKth( a ,  n ,  K ) {
    // write code here
    return QuickSort(a, 0, n - 1, K)
}

function QuickSort(arr, s, t, k) {
    let left = s + 1, right = t, x = arr[s];

    while(left <= right) {
        while(left <= right && arr[left] >= x) left++;
        while(left <= right && arr[right] <= x) right--;

        if(left < right) {
            let t = arr[left];
            arr[left] = arr[right];
            arr[right] = t;
        }
    }

    if(s != right) {
        arr[s] = arr[right];
        arr[right] = x;
    }

    if(right == k -1) {
        return arr[right];
    } else if(right > k - 1) {
        return QuickSort(arr, s, right - 1, k);
    } else {
        return QuickSort(arr, right + 1, t, k);
    }
}
module.exports = {
    findKth : findKth
};

五、 最小的K个数

给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可)。

输入:[4,5,1,6,2,7,3,8],4 
返回值:[1,2,3,4]
说明:返回最小的4个数即可,返回[1,3,2,4]也可以  

1. 思路

      观察可以发现,只需要将数组进行排序,再使用一个for循环进行数据的输出即可。为了简便,直接使用内置的方法进行排序。

	input.sort((a, b) => {
        return a - b;
    });

2. 代码实现

function GetLeastNumbers_Solution(input, k) {
    // 将传进的数组进行排序
    input.sort((a, b) => {
        return a - b;
    });
    // 初始化结果数组变量
    let res = [];
    // 将结果存进res数组中
    for (let i = 0; i < k; i++) {
        res.push(input[i]);
    }
    // 返回结果
    return res;
}
module.exports = {
    GetLeastNumbers_Solution: GetLeastNumbers_Solution,
};

六、instanceof

      请补全JavaScript代码,要求以Boolean的形式返回第一个实例参数是否在第二个函数参数的原型链上。

1. 思路

      由于只看过原型和原型链的讲解视频,并没有进行对应的知识实践,做开始即使是看了题解也不是很明白代码表达的意思。最后通过代码理解,知识回顾等进行代码设计。
附上原型和原型链的关系图。
JavaScript实现全排列_第3张图片

2. 代码实现

      对对象原型和原型链有了更深的理解。

const _instanceof = (target, Fn) => {
                // 先判断传进的target是否为一个对象 
                if(typeof target !== 'object' || target === null) return false;
                // 获取target对象中的对象原型__proto__
                let proto = target.__proto__;
                 // 使用while循环逐层向上寻找__proto__
                while(proto) {
                    // 当符合条件返回结果true
                    if(proto === Fn.prototype) return true;
                    // 不断向上寻找,直到找到结果或null
                    proto = proto.__proto__;
                }
                return false;
            }

你可能感兴趣的:(算法,javascript,面试)