01-线性表-算法
线性表相关算法
Java Array
11. 盛最多水的容器
思路:
1、蛮力法:
将所有的组合全部计算,找出其中最大值;
分析:时间复杂度:O(n2) [因为需要
n * (n +1) / 2次组合,所以复杂度],空间复杂度:O(1),常数级,因为没有辅助数组;
2、双指针法:
在数组双端设置两个标记量,视作两个指针;保留指向的两个值中更大的那个数值,另一个指针向中间移动,再判断组成面积的大小,更大则记录,否则不记录;继续循环,直到两个指针相遇。
分析:时间复杂度:O(n) [因为只需要便利一次数组即可],空间复杂度:O[1],常数级,理由同上。
注:之所以需要保留更大的数值,因为围成的面积是由两指针数据较小值和两指针的距离有关,移动指针会减小两指针间距离,只需要保留较大的指针数值,就能保证之后围成的面积会尽可能保留更大值;相关代码 [双指针法]:点击查看题解
class Solution { public int maxArea(int[] height) { int front = 0, after = height.length - 1; // 这里定义双指针 int max_area = 0, area = 0, high = 0; // 这里定义的是 最大面积、过程中的面积 和 过程中的高度 while (front < after) { // 循环条件,两指针不相遇 int wide = after - front; high = height[front] > height[after] ? height[after--] : height[front++]; // 用三元表达式判断大小,不用 Math 工具类,可以在判断大小的时候顺便将指针移动了,自增、自减符号放在后面即是先执行此步骤再执行自增自减; area = wide * high; if (area > max_area) // 判断过程量是否比之前的最大值更大,是则改变最大值 max_area = area; } return max_area; } }另一个思路:
class Solution { public int maxArea(int[] height) { int res = 0; // 定义将返回的结果,即最大面积 int right = height.length-1, left = 0, hight; // 定义双指针 和 过程中高度 while (left<right){ // 循环条件仍是两指针不相遇 if(height[left]>height[right]){ // 判断两指针指向的数值大小 hight = height[right]; // 记录较小值的大小, res = Math.max(hight*(right-left),res); // 判断、记录最大值 while(left<right&&hight>=height[right]) // 在做右指针未相遇 且没有之前的 记录值hight 更大,则移动指针 right--; } else { hight = height[left]; res = Math.max(hight*(right-left),res); while (left<right&&hight>=height[left]) // 原理同上 left++; } } return res; } }
283. 移动零
思路:
1、蛮力法:
思路反而复杂,不推荐;因为遇到
一个零和多零并排的时候情况不同,可能需要多次循环消除多零并排的影响;时空复杂度,因为没有具体算法思路,暂不讨论;
2、计数零法 [count zeros]
用一个变量记录 0 出现的次数,将对应数向前移动对应的位,并将原位置标零;
时间复杂度:O(n),空间复杂度:O(1)
对应代码:
class Solution { public void moveZeroes(int[] nums) { if (nums == null || nums.length == 0) return ; int count = 0; // 用于记录 0 出现的次数 for (int i = 0; i < nums.length; i++) { if (nums[i] == 0) // 如果出现了 0 值,count 进行记录 count++; else { // 将所有的 非零数 前移 count 位,并将遍历到的位置置零 if (count == 0) // 如果 count 为零就不需要进行操作了,否则可能出现错误 continue; nums[i - count] = nums[i]; nums[i] = 0; } } } }
3、双指针法:点击查看题解
设置两个指针,一个用于记录非零数,一个正常遍历,当两个指针指向不同时,将非零数直接赋值给正常遍历数;
时间复杂度:O(n),空间复杂度:O(1);
代码示例:
class Solution { public void moveZeroes(int[] nums) { if (nums == null || nums.length == 0) { return; } int zero = 0; // 这里为正常遍历的指针 for (int _zero = 0; _zero < nums.length; _zero++) { // 这里可以看作 寻找非零数的指针 if (nums[_zero] != 0) { if (_zero > zero) { // 如果非零数零数的后面,则说明有零,更换两指针指向的位置的值 nums[zero] = nums[_zero]; nums[_zero] = 0; } zero++; } } } }
70. 爬楼梯
思路
先不管具体使用什么思路,首先需要将本题进行抽象,到底在问什么问题;根据已有的数据,可以了解这道题其实就是一个具象化的斐波拉契数列;我们假设第 k 级台阶 有 ak 种走法,则:
a1 = 1; a2 = 2;
ak = ak-1 + ak-2 [k >= 3];
因为,
第k级台阶的走法只能从第 k - 1 级台阶和第 k - 2 级台阶走过去,所以我们可以根据这个完成相关的算法设计:
相关代码:迭代法
class Solution {
public int climbStairs(int n) {
if (n <= 2) return n; // 如果只问前两层的直接返回对应值;因为我们知道前两级的走法正好等于它的级数
int f = 1, r = 2, tmp = 0; // 设置三个变量,准备迭代,tmp 用于承接空值,f 为相邻两级台阶的前一级的走法,r 为后一级
int l = 3; // l 即 level,表示的是正在求的层数
while (l++ <= n) { // 使用 l++ 而不是 ++l 是因为我们的级数设计是从 3 开始,而第三级台阶的走法在之前的数据中并没有计算出来
tmp = r; // 使用辗转相加,r 一直表示后一级台阶的走法数量,f 表示前一级
r = f + tmp;
f = tmp;
}
return r; // 最后跳出循环的 r 即为所求
}
}
算法分析:时间复杂度:O(n),一次循环;空间复杂度:O(1),使用的是常量级的变量;
方法二:记忆化存储[斐波拉契数列的常规解法]
class Solution {
private int[] stairsCount = null; // 设置一个全局变量,记录各级台阶的走法
public int climbStairs(int n) {
if (n <= 2) return n; // 简化运算
stairsCount = new int[n]; // 生成对应位数的记录数据
countMethod(n - 1); // 调用递归填充数组
return stairsCount[n - 1]; // 返回 第 n 级台阶的数,因为索引是从零开始的
}
public int countMethod(int n) {
if (n < 2) // n < 2 的时候用 返回其对应的数据,并存入记忆化数组中
return stairsCount[n] = n + 1;
if (stairsCount[n] == 0) // 递归调用,返回对应值
stairsCount[n] = countMethod(n - 1) + countMethod(n - 2);
return stairsCount[n]; // 返回当前层数据
}
}
在递归使用的过程中,希望大家能想象一下它的数据下探模式,在
17行其实就已经在调用递归函数,在此函数未结束的时候调用新函数,在下探到第 0 \ 1 的索引时获取确定的数据,再将之返回,完成数据填充;在调用同行第二个函数的时候,所有值都已经填充完毕,所以只需消耗获取数组索引的时间;算法分析:
时间复杂度:O(n),其实只下探了一次,其余数据可以直接根据数组获得;空间复杂度:O(n),使用了线性的辅助内存空间。
15. 三数之和
思路
1、蛮力法:直接三层嵌套递归,逐一比对;
分析:时间复杂度:O(n3),因为需要三层递归;空间复杂度:O(1),只是用了常量级的额外空间;
时间复杂度太高,不推荐,愿意的话可以自行完成相关代码;
2、双指针法:
首先将数组进行排序,调用方法类,时间复杂度为 O(n log2n);因为要找和为 0 的三个数,且数组有序;所以选择定一变二,先定住一个数,再在其他数中找和为此数相反数的两个数;这个定住的数按照脚标 [或者说索引] 依次更迭,且定住的数和前一次不能相同;找相反数的时候使用双指针法,在 O(n) 的时间复杂度下完成数据查找;
相关代码:力扣国际版讨论
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans = new ArrayList<>(); // 返回的数据,根据题目设置
int len = nums.length;
if (nums == null || len < 3) return ans; // 前置判定
Arrays.sort(nums); // 数据排序
for (int i = 0; i < len - 2; i++) { // 三个数,所以不需要 len
if (nums[i] > 0) return ans; // 负数时候已经计算了正数了,没必要二次记录
if (i > 0 && nums[i] == nums[i - 1]) continue; // 定的数和上一次定的数不相重合
int lo = i + 1, hi = len - 1, sum = 0 - nums[i]; // 设置双指针,和目标值
while (lo < hi) {
if (nums[lo] + nums[hi] == sum) {
ans.add(Arrays.asList(nums[i], nums[lo], nums[hi])); // 满足需求,添加到返回值中
while (lo < hi && nums[lo+1] == nums[lo]) lo++; // 为了保证不会出现重复数据
while (lo < hi && nums[hi-1] == nums[hi]) hi--; // 同上
lo++;
hi--;
} else if (nums[lo] + nums[hi] < sum) { // 如果和值小于目标值,需要将较小值指针后移
lo++;
} else { // 和值大于目标值,较大值指针前移
hi--;
}
}
}
return ans;
}
}
Java List
206. 反转链表
思路
使用迭代,依次倒置节点
能力有限,做不出图解,有兴趣的可以去看看相关的,都是力扣的精选题解;
相关代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode prev = null, curr = head; // 前置节点和当前节点
while (curr != null) { // 只要当前节点不为空则需要继续倒置
ListNode nextTemp = curr.next; // 记录当前节点的下一节点
curr.next = prev; // 将当前节点的后置节点改为前置节点
prev = curr; // 将当前节点付给前置节点变量,准备下一循环
curr = nextTemp; // 当前节点赋值为原节点的后置节点 nextTemp
}
return prev; // prev 即为倒置链表的头节点
}
}
24. 两两交换链表中的节点
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null)
return head;
ListNode front = head, behind = head.next;
front.next = swapPairs(behind.next);
behind.next = front;
return behind;
}
}
/*
class Solution {
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null) return head;
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode front = null;
ListNode after = null;
ListNode pre = dummy;
while (head != null && head.next != null) {
front = head;
after = head.next;
pre.next = after;
front.next = after.next;
after.next = front;
pre = front;
head = pre.next;
}
return dummy.next;
}
}
*/
后续持续更新,一直刷算法写题解挺难顶的,见谅哈,逐步更新

浙公网安备 33010602011771号