今天和我一个宿舍的小姑娘离职了,搬家搬了一天。收拾东西打包来回倒腾。感觉自此一别,不出意外也就是一辈子了。倒也没多伤感,大概习惯了吧。
生活就像坐火车,
身边的人去去留留,停停走走
有人上车就会有人下车
有人坐着就会有人站着
安心旅途,安静生活
好了,开始刷题了!
对链表进行插入排序。
题目:对链表进行插入排序。插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。
思路:这个图我也弄不过来,大概讲一下吧,是个升序排列,从第二个元素开始,挨个比对,按升序插入。然后第三个第四个逐次往下排序。然后排序的队列也越来越长,直到最后一个也插入到应该插入的为止既排好序了。如果这个是数组比较那么简直是伸手就来,但是这个题是链表。还好没什么时间空间复杂度的要求,我觉得也不是很难做。我目前的想法就是一个链表顺着往下挑节点,另一链表是链表头,然后从前往后选择插入点。具体怎么做我去写代码试试.
好了,做完出来了,因为这个链表来回来去插入和取出很麻烦,所以我注释写的很清楚,不断微调好久。。我直接贴代码了:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode insertionSortList(ListNode head) {
ListNode first = new ListNode(0);
first.next = head;
//当head.next等于null说明没有需要插入排序的节点了
while(head != null && head.next != null){
if(head.val<= head.next.val){//已经是升序不用排了
head = head.next;
}else{
ListNode temp = first;
//从头结点开始,找到应该插入的位置(第一个比他大的节点的前一个)
while(temp.next.val<head.next.val) temp = temp.next;
ListNode cur = head.next;
head.next = cur.next;
//上两句的作用是head.next = head.next.next.把head的下一个跳过去了并存在了cur中
cur.next = temp.next;
temp.next = cur;
//这两句是把 cur插在了 temp 和 temp.next之间
}
}
return first.next;
}
}
然后这个性能超过了百分之九十八的人,一次及格,所以我也不多看了,直接下一题。
排序链表
题目:在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
思路:讲真,这块链表的题都不算太难啊。然后这个题排序是次要的,甚至上道题也排序了啊。重点是常量空间复杂度,和O(n)时间复杂度啊。logn是什么概念?我记得二分法好像就是吧?说到二分,用归并么?啧啧,这个题我先理理思路。看看归并能不能实现吧。
有点绝望,我怎么想也没相处来O(n)时间复杂度,哪怕归并也没用。。我还是先随缘实现下,然后看题解吧。
好了,用的归并完成的,我先贴代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode sortList(ListNode head) {
return mergeSort(head);
}
public ListNode mergeSort(ListNode head){
if(head==null || head.next==null) return head;
ListNode f = head;
ListNode s = head;
ListNode p = null;
while(f != null && f.next != null){
p = s;
s = s.next;
f = f.next.next;
}
//这一步是使得head 和 s 将一个完整的链表中间拆成两段
p.next = null;
ListNode l = mergeSort(head);
ListNode r = mergeSort(s);
p = new ListNode(0);
ListNode res = p;
while(l != null && r != null){
if(l.val<r.val) {
p.next = l;
p = p.next;
l = l.next;
} else {
p.next = r;
p = p.next;
r = r.next;
}
}
if(l != null){
p.next = l;
}
if(r != null){
p.next = r;
}
return res.next;
}
}
完美的没用额外空间(说起来上一道插入的那个题也没用额外空间)至于时间复杂度就这样吧,我去看看性能第一的代码了。
额,有点失望。。。不是有点,是大大大大大的失望了,性能排行第一的是什么玩意,不仅时间复杂度不行,空间复杂度都没做到。。。我贴出来下,不用瞻仰:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null)
return head;
ListNode cur = head;
int count = 0;
//获取长度
while (cur != null) {
count++;
cur = cur.next;
}
int[] arr = new int[count];
int index = 0;
cur = head;
//值导入数组
while (cur != null) {
arr[index++] = cur.val;
cur = cur.next;
}
//排序
Arrays.sort(arr);
cur = head;
index = 0;
//重新赋值
while (cur != null) {
cur.val = arr[index++];
cur = cur.next;
}
return head;
}
}
然后接下来去看看题解了,找找别人的思路:
呵,千篇一律归并,总算有一个说不是归并还是c++写的。。。有点看不懂。。这个题可能就是这么无解的吧。。。我就过了。下一题了。
逆波兰表达式求值
题目:根据逆波兰表示法,求表达式的值。有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:整数除法只保留整数部分。给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
思路:这个题怎么说呢,我看了半天这个表达式,总算是有点懂的意思了。首先,一个数字后面紧跟着一个符号,说明是X 符号 数字 这么个表达式。 而X是未知数,具体是什么还要看前面如果是数字则是数字,如果是符号说明X也是一个表达式。反正就是这个意思。我感觉做法应该是有能进阶的就立刻进阶。最开始的那个符号对应的肯定是两个数字。。语言表达不清楚,我去实现试试
好了,做出来了,1110ms,性能吓人:
class Solution {
public int evalRPN(String[] tokens) {
if(tokens.length == 0) return 0;
List<String> list = new ArrayList<>(tokens.length);
for (String s : tokens) {
list.add(s);
}
dfs(list);
return Integer.parseInt(list.get(0));
}
public void dfs(List<String> list){
if(list.size()==1) return;
int n = 0;
int idx = 0;
for(int i = 0;i<list.size();i++){
if(list.get(i).equals("+")){
int f = Integer.parseInt(list.get(i-2));
int s = Integer.parseInt(list.get(i-1));
n = f + s;
idx = i;
break;
}
if(list.get(i).equals("-")){
int f = Integer.parseInt(list.get(i-2));
int s = Integer.parseInt(list.get(i-1));
n = f - s;
idx = i;
break;
}
if(list.get(i).equals("*")){
int f = Integer.parseInt(list.get(i-2));
int s = Integer.parseInt(list.get(i-1));
n = f * s;
idx = i;
break;
}
if(list.get(i).equals("/")){
int f = Integer.parseInt(list.get(i-2));
int s = Integer.parseInt(list.get(i-1));
n = f / s;
idx = i;
break;
}
}
list.set(idx,String.valueOf(n));
list.remove(idx-1);
list.remove(idx-2);
dfs(list);
}
}
因为第一版本比较无脑,我主要是看思路是不是对的,应该是有别的办法。
自己优化半天性能也不能,最好也是290.所以我直接看大佬思路了。
我直接贴出来,是从后往前递归,我甚至觉得有点动规的意思了。。哎,还是思路不行,我先贴代码:
class Solution {
public int evalRPN(String[] tokens) {
index = tokens.length - 1;
return getPrefix(tokens);
}
int index;
public int getPrefix(String[] tokens) {
String token = tokens[index--];
if (token.equals("+")) {
int prefix1 = getPrefix(tokens);
int prefix0 = getPrefix(tokens);
return prefix0 + prefix1;
} else if (token.equals("-")) {
int prefix1 = getPrefix(tokens);
int prefix0 = getPrefix(tokens);
return prefix0 - prefix1;
} else if (token.equals("*")) {
int prefix1 = getPrefix(tokens);
int prefix0 = getPrefix(tokens);
return prefix0 * prefix1;
} else if (token.equals("/")) {
int prefix1 = getPrefix(tokens);
int prefix0 = getPrefix(tokens);
return prefix0 / prefix1;
} else {
return Integer.parseInt(token);
}
}
}
一看就会,一写就废系列。其实代码不是多难,但是我就没想到这么从后往前。而是傻傻的从前往后。并且为此用了list,来回来去删除重写。
总的来说今天的三道题比较简单,然后就这样了吧。
今天的笔记就记到这里,如果稍微帮到你了记得点个喜欢点个关注。也祝大家工作顺顺利利,生活健健康康!周末愉快!