一、数组
- 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。【二分查找】
// 方法一:
var search = function(nums, target) {
let left = 0;
let right = nums.length - 1;
let mid;
while(left <= right) {
mid = left + (right - left) - 1;
if (nums[mid] > target) { // 大于目标值
right = mid - 1;
} else if (nums[mid] < target) { // 小于目标值
left = mid + 1;
} else {
return mid;
}
}
return -1;
};
// 总结: 会超时中间的代码还需要进行优化
// 方法二:
var search = function(nums, target) {
let left = 0, right = nums.length - 1, mid;
while(left < right) { // 条件结束的时候left = right,不需要判断是否相等
mid = left + Math.floor((right - left) / 2);
if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
return mid;
}
}
return nums[left] == target ? left : -1; // 第一次取mid值之后,下一次都是判断nums[left]是否和target相等,可以自己手写验证下,所以这里是判断nums[left]的值
};
- 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
例如:
输入: num = [2, 3, 2, 4, 2], val = 2;
输出: 2, [3, 4] 是留下来的元素
< !--
解题思路:
1. 使用双层for循环的暴力解法
2. 使用数组的splice方法
3. 使用双指针方法
-->
// 方法一:暴力解法
var removeElement = function(nums, val) {
// 定义i,j,两个for循环判断是否相等
let i,j;
for (i = 0; i < nums.length; i++) {
if (nums[i] == val) {
for (j = i + 1; j < nums.length; j++) {
nums[j - 1] = nums[j];
}
i--;
nums.length--;
}
}
return nums.length;
};
// 方法二:使用数组的splice方法
var removeElement = function(nums, val) {
for (let i = 0; i < nums.length; i++) {
if (nums[i] == val) {
nums.splice(i, 1); // 移除当前的重复值
i--;
}
}
return nums.length;
}
// 方法三:使用双指针法,
var removeElement = function(nums, val){
const len = nums.length;
let left = 0, right;
for (right = 0; right < len; right++) {
if (nums[right] !== val) {
nums[left] = nums[right]; // 判断快指针的值是否是val,不是的话就将right指向的元素赋值给left进行覆盖
left++;
}
}
return left; // left的长度就是最终的新数组长度
}
- 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
例如:
输入: nums = [1,3,5,6], target = 5
输出: 2
// 第一种方法: 二分查找法
var searchInsert = function(nums, target) {
// 首先判断两个端点的值是否包含target
if (nums[0] > target) {
return 0;
} else if (nums[nums.length - 1] < target) {
return nums.length; // 这里不需要返回Nums.length - 1;
}
let len = nums.length - 1;
let left = 0;
while (left <= len) {
let mid = Math.round((left + len) / 2);
if (nums[mid] > target) {
left = mid - 1;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
return mid;
}
}
return left;
};
// 第二种方法:直接遍历整个数组看是否有和target相等的元素,如果有就返回当前元素的索引值
var searchInsert = function(nums, target) {
for (let i = 0; i < nums.length; i++) {
if (nums[i] == target) return i;
}
return nums.length;
}
- 给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。你可以假设除了整数 0 之外,这个整数不会以零开头。
例如:
输入[1, 3, 4, 8] 或者 [1, 2, 9]
输出: [1, 3, 4, 9] 或者 [1, 3, 0]
// 方法一: 使用for循环倒着遍历,然后当前元素和10取模判断是否存在,存在就返回数组
var plusOne = function(digits) {
for (let i = digits.length - 1; i >=0; i--) {
digits[i]++;
digits[i] = digits % 10;
if (digits[i]) return digits;
}
// 这里适用于特殊情况 [9]
digits.unshift(1);
return digits;
}
// 方法二:循环遍历判断是否当前元素值小于9
// < 9 当前元素就自增,同时返回数组
// > 9 当前元素的值就为0,返回[1, ...digits]
var plusOne = function(digits) {
for (let i = digits.length - 1; i >= 0; i--) {
if (digits[i] < 9) {
digits[i]++;
return digits;
}
// 不小于9,说明可能刚好这个元素就是9,让它的值为0
digits[i] = 0;
}
return [1, ...digits];
}
- 给定两个数组,arr1 和 arr2,其中arr1里面的元素都存在于arr2里面,但是arr1里面还有没存在于arr2里面的元素;将重复的元素输出,同时将不存在与arr2里面的元素进行升序之后再输出
例如:
输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6]
输出:[2,2,2,1,4,3,3,9,6,7,19]
// 方法一: 遍历 + 过滤 + 排序
var relativeSortArray = function(arr1, arr2) {
let newArr = [];
// 1. 遍历找出arr1里面和arr2相同的元素,如果存在就push到新数组里面存储,包含重复的元素
for (var i = 0; i < arr2.length; i++) { // 外层循环必须是arr2
for (var j = 0; j < arr1.length; j++) {
if (arr2[i] == arr1[j]) {
newArr.push(arr1[j])
}
}
}
// 2.将arr1数组进行过滤(留下新数组里面不存在的值),然后将过滤出来的值进行排序添加到新数组当中
newArr.push(...arr1.filter(item => newArr.indexOf(item) == -1).sort(a, b) => a -b));
return newArr;
}
- 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
例如:
输入:[7,1,5,3,6,4]
输出: 5
解析: 在第二天的时候买入(i = 1) 在第五天的时候卖出(i = 6) 这样才能获取到最大利润5
// 方法一: 使用Math.max / min 方法来进行计算最大利润
var maxProfit = function(prices) {
if (prices.length < 2) return 0;
let res = 0, minPrices = prices[0]; // 假设第一个值就为最小值
for (var i = 1; i < prices.length; i++) {
res = Math.max(res, prices[i] - minPrices); // 取出后一个元素减去前一个元素的最大值
minPrices = Math.min(minPrices, prices[i]); // 取出当前元素和minprices对比的最小值
}
return res;
}
// 方法二:定义一个最小值,遍历是否元素i小于这个最小值,如果不小于就取出大的值就是最大利润
var maxProfit = function(prices) {
let min = Number.MAX_SAFE_INTEGER; // MAX_SAFE_INTEGER是JavaScript中的一个常量,它表示JavaScript中可以安全表示的最大整数。 (不去深究,定义就行)
let max = 0;
for (var i = 0; i < prices.length; i++) {
if (prices[i] < min) {
min = prices[i];
} else {
max = Math.max(max, prices[i] - min);
}
}
return max;
}
- 给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素。
例如:
输入: [1, 2, 3, 3, 3, 3, 2]
输出: [3]
// 方法一:排序
var majorityElement = function(nums) {
nums.sort((a, b) => a - b);
return nums[Math.floor(nums.length / 2)]; // 排序之后返回数组的中间那个数一定就是出现次数最多的那个元素
}
// 方法二:搞个对象
var majorityElement = function(nums) {
let half = nums.length / 2;
let obj = {};
for (const num of nums) {
obj[num] = (obj[num] || 0) + 1; // 将遍历的所有元素存储到对象里面 键:该元素 值:出现的次数
if (obj[num] > half) return num; // 如果该元素的次数大于half就返回
}
}
二、链表
- 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
例如:
输入: list1 = [1, 2, 4, 9] list2 = [2, 4, 4, 5];
输出: list_result = [1, 2, 2, 4, 4, 4, 5, 9];
// 方法一: 使用递归的方法
var mergeTwoLists = function(list1, list2) {
if (list1 == null) return list2;
if (list2 == null) return list1;
if (list1.val > list2.val) { // 这里不能写list1.value 链表
list2.next = mergeTwoLists(list2.next, list1);
return list2;
} else {
list1.next = mergeTwoLists(list1.next, list2);
return list1;
}
}
递归注意点:
- 该问题的子问题是什么?
- 当前层需要做什么? 一定不要去想下一层,下下层…
- 递归结束的条件?
以上题目都来自于leetcode,如果需要更加详细的算法学习可以上https://leetcode.cn,谢谢!