11/5
今天看的有jvm内容,回溯算法,还有排序
先总结排序,今天做的是插入排序和选择排序,至于冒泡排序就不写了。
插入排序做的是LeetCode的题目:
147. 对链表进行插入排序
插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/insertion-sort-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
插入排序算法:
插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
重复直到所有输入数据插入完为止。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/insertion-sort-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码思路:跟普通的插入排序思想一样,在循环到的元素下标之前找出比这个元素更大的元素,把这两个元素进行交换,注意这个交换是在把前面排序好的元素一一遍历交换的!直至遍历到第一个元素。
在写这道代码题之前,先写下普通数组的插入排序:
public class Solution {
public int[] sortArray(int[] nums) {
int len = nums.length;
for (int i = 1; i < len; i++) {
//遍历之前的全部元素!
for (int j = i; j > 0; j--) {
// 注意:前面的数严格大于后面的数才交换
if (nums[j - 1] > nums[j]) {
swap(nums, j, j - 1);
} else {
break;
}
}
}
return nums;
}
private void swap(int[] arr, int index1, int index2) {
int temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
}
然后进行这道题的分析,首先这道题是一个单向链表,所以第一个问题是如何进行前面节点的遍历,这里首先要把头结点变成普通节点,所以先进行了创建一个新的“头结点”,然后这个新的头结点在每一次循环之后都回归到头结点位置,这样我们就可以在每一次循环都从头结点到下一个循环的节点,然后再创建两个检查指针节点,一个表示当前循环到哪个节点,一个表示当前循环的下一个节点。
代码如下:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode insertionSortList(ListNode head) {
if (head==null ||head.next==null)
return head;
ListNode flag=new ListNode(-1);
flag.next=head;//创建新的头结点
ListNode pre=head;//当前循环到的节点
ListNode cur=head.next;//循环到的下一个节点,进行比较
while(cur!=null){//遍历到最后的一个节点
if(pre.val<cur.val){//如果这两个节点是升序的,就无须再进行排序了,直接进行下一个节点的遍历
pre=pre.next;
cur=cur.next;
}else{//当下一个节点的数值比当前节点要小的时候,进行排序
ListNode p=flag;//重置头结点
while(p.next!=null&&p.next.val<cur.val){
p=p.next;//从头结点遍历到当前的节点,寻找比cur要大的节点
}
//下面四条语句是我个人觉得比较难理解的,举个例子说明假设链表为134253,cur.val=2
pre.next=cur.next;//把cur节点先从链表中断开出来,链表现在为13453,2单独出来
cur.next=p.next;//把遍历到的比cur大的节点放在原有cur的位置,链表现在为13453,2单独出来
p.next=cur; //跟上一步对应,将这两个节点交换,链表现在为123453
cur=pre.next;//进行下一节点的循环,cur.val现在为5
}
}
return flag.next;
}
}
- 排序数组
给你一个整数数组 nums,请你将该数组升序排列。
示例 1:
输入:nums = [5,2,3,1]
输出:[1,2,3,5]
示例 2:
输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-an-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public int[] sortArray(int[] nums) {
for(int i=0;i<nums.length-1;i++){
int minindex=i;
for(int j=i+1;j<nums.length;j++){
if(nums[j]<nums[minindex])
minindex=j;
}
swap(nums,i,minindex);
}
return nums;
}
public void swap(int[] nums,int index1,int index2){
int temp=nums[index1];
nums[index1]=nums[index2];
nums[index2]=temp;
}
}
没什么好说的,保存这道题,学完所有排序都试一下。
然后是回溯的大boss,今天是DFS的入门基础
第一道题
46. 全排列
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
思路就是列出树结构,用dfs来进行,中间有回溯,具体代码解释和思路liweiwei1419大佬写的很明白,我就不多赘述了。
代码如下:
class Solution {
public List<List<Integer>> permute(int[] nums) {
int len= nums.length;
boolean[] userd=new boolean[len];
var res=new ArrayList<List<Integer>>();
if (len==0){
return res;
}
List<Integer> path = new ArrayList<>();
dfs(nums,len,0, path,userd,res);
return res;
}
private void dfs(int[] nums, int len, int depth,
List<Integer> path, boolean[] used,
List<List<Integer>> res) {
if (depth==len){
res.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < len; i++) {
if (!used[i]) {
path.add(nums[i]);
used[i] = true;
dfs(nums, len, depth + 1, path, used, res);
used[i] = false;
path.remove(path.size()-1);
}
}
}
}
这是我写的第一道dfs算法题,首先是要写出树结构的所有可能,然后再分析其中的状态变量,这种算法真的巧妙啊。。。
然后第二道题跟上一道差不多,限定了输入可以重复,输出不能重复。
47. 全排列 II
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
示例 2:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
我一开始看到,心里想,这不是在判断里面加一个contains方法就可以了吗?!然后我也试了,确实可以,但是差点超时,绝了。
然后这道题的正确解法姿势是剪枝,具体操作也是看那位大佬
代码如下:
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
int len = nums.length;
List<List<Integer>> res = new ArrayList<>();
if (len == 0) {
return res;
}
// 排序(升序或者降序都可以),排序是剪枝的前提
Arrays.sort(nums);
boolean[] used = new boolean[len];
// 使用 Deque 是 Java 官方 Stack 类的建议
Deque<Integer> path = new ArrayDeque<>(len);
dfs(nums, len, 0, used, path, res);
return res;
}
private void dfs(int[] nums, int len, int depth, boolean[] used, Deque<Integer> path, List<List<Integer>> res) {
if (depth == len) {
res.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < len; ++i) {
if (used[i]) {
continue;
}
// 剪枝条件:i > 0 是为了保证 nums[i - 1] 有意义
// 写 !used[i - 1] 是因为 nums[i - 1] 在深度优先遍历的过程中刚刚被撤销选择
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
continue;
}
path.addLast(nums[i]);
used[i] = true;
dfs(nums, len, depth + 1, used, path, res);
// 回溯部分的代码,和 dfs 之前的代码是对称的
used[i] = false;
path.removeLast();
}
}
}
今天的算法题营养很丰富,希望能够牢记。然后jvm内容的话,今天其实看得也不多,感觉没到记下的程度,所以说今天还是摸鱼了?!但是算法整的我有点头大。