1 空间和时间复杂度
资料来源:力扣《图解算法数据结构》
N:输入数据量、数据大小
算法复杂度:分为时间复杂度和空间复杂度
时间复杂度:指输入数据大小为 N 时,算法运行所需花费的时间,这个时间是算法云习惯的“计算操作的数量”,而非绝对时间
N:算法处理的输入数据量(排序算法下是需要排序的元素数量,搜索算法是搜索范围的元素总数)
大O是最常使用的时间复杂度评价渐进符号
空间复杂度:在最差的情况下,算法运行所使用的最大空间
通常情况下,空间复杂度指在输入数据大小为 N 时,算法运行所使用的「暂存空间」+「输出空间」的总体大小。
空间复杂度涉及的空间类型有:
输入空间: 存储输入数据所需的空间大小;
暂存空间: 算法运行过程中,存储所有中间变量和对象等数据所需的空间大小;
输出空间: 算法运行返回时,存储输出数据所需的空间大小;
2 数据结构
常见的数据结构可分为「线性数据结构」与「非线性数据结构」,具体为:「数组」、「链表」、「栈」、「队列」、「树」、「图」、「散列表」、「堆」。
数组
数组是将相同类型的元素存储于连续内存空间的数据结构,其长度不可变。
如下图所示,构建此数组需要在初始化时给定长度,并对数组每个索引元素赋值
链表
链表以节点为单位,每个元素都是一个独立对象,在内存空间的存储是非连续的。链表的节点对象具有两个成员变量:「值 val」,「后继节点引用 next」 。
栈
栈是一种具有 「先入后出」 特点的抽象数据结构,可使用数组或链表实现。
队列
队列是一种具有 「先入先出」 特点的抽象数据结构,可使用链表实现。
树
树是一种非线性数据结构,根据子节点数量可分为 「二叉树」 和 「多叉树」,最顶层的节点称为「根节点 root」。以二叉树为例,每个节点包含三个成员变量:「值 val」、「左子节点 left」、「右子节点 right」 。
图
图是一种非线性数据结构,由「节点(顶点)vertex」和「边 edge」组成,每条边连接一对顶点。根据边的方向有无,图可分为「有向图」和「无向图」。
散列表
散列表是一种非线性数据结构,通过利用 Hash 函数将指定的「键 key」映射至对应的「值 value」,以实现高效的元素查找。
堆
堆是一种基于「完全二叉树」的数据结构,可使用数组实现。以堆为原理的排序算法称为「堆排序」,基于堆实现的数据结构为「优先队列」。堆分为「大顶堆」和「小顶堆」,大(小)顶堆:任意节点的值不大于(小于)其父节点的值。
数组相关的算法习题
3 LC485. 最大连续1的个数
给定一个二进制数组 nums
, 计算其中最大连续 1
的个数。
【动态规划】
var findMaxConsecutiveOnes = function(nums) {
// max: 记录当前次数的最大值, count: 记录当前1连续出现的次数
var max = 0; count = 0;
for (var i = 0; i < nums.length; i ++) {
// nums[i] == 1, count++
if (nums[i] === 1) count++;
// 否则,count重新计数
else count = 0;
// 重置当前次数的最大值
max = Math.max(max, count);
}
return max;
};
方法二:转换数据类型,求最长字符串长度
既然数组中只有0和1,还可以以元素0作为分割点将原数组转换成多个只包含1的字符串数组:
[1, 1, 0, 1, 1, 1] => '110111' => ['11', '111']
这样就变成了求转换后数组中最长字符串长度。
Array.sort从长到短排序
var findMaxConsecutiveOnes = function(nums) {
// 将数组以0分割 ['11', '111']
var temp = nums.join('').split('0');
// 获取数组中最长的字符串
var max = temp.sort((a, b) => b.length - a.length)[0]return max.length;
};
Math.max取长度最大值
var findMaxConsecutiveOnes = function(nums) {
// 将数组以0分割 ['11', '111']
var temp = nums.join('').split('0');return Math.max(...(temp.map(item => item.length)));
};
4 LC283. 移动零
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
答案一:双指针
思路二:双指针
慢指针 j 从 0 开始,当快指针 i 遍历到非 0 元素的时候,i 和 j 位置的元素交换,然
后把 j + 1;
也就是说,快指针 i 遍历完毕后, [0, j) 区间就存放着所有非 0 元素,而剩余的[j,
n]区间再遍历一次,用 0 填充满即可。
var moveZeroes = function (nums) {
let j = 0for (let i = 0; i < nums.length; i++) {
if (nums[i] !== 0) {
nums[j] = nums[i]
j++
}
}while (j < nums.length) {
nums[j] = 0
j++
}
}
答案二:
var moveZeroes = function(nums) {
//获取数组长度
let len = nums.length;
//循环遍历数组
for(let i =0;i<len;i++){
//当数组哪个位置是0 ,就删除这个这个位置的元素,然后在末尾补 0
//从而就达到了 把所有0移动到数组末尾的要求且保持的非零元素的相对顺序
if(nums[i]==0){
nums.splice(i,1)
nums.push(0)
//当为0的元素删除后,下一个元素就会前进一位占据该位置,所以要从该位置在进行判断
i--
//当移动到末尾的元素,就不用再一次进行遍历了,所以遍历的长度要减去1位
len--
}
}
return nums
}
5 LC27. 移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
第一次通过测例耶耶耶
var removeElement = function(nums, val) {
for (var i = 0; i<nums.length ; i++){
if ( nums[i] == val) {
nums.splice(i,1);
i--
}
}
return nums.length;
};