1. 反转链表
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
两种方法 :1是循环,2是递归
Java答案:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
1. 循环
class Solution {
public ListNode reverseList(ListNode head) {
if(head==null){
return null;
}
ListNode p = head.next;
head.next=null;
while(p!=null){
ListNode q = p;
p=p.next;
q.next = head;
head = q;
}
return head;
}
}
执行时间:1ms,内存消耗:35.6MB
2. 递归:两个节点反转就是head.next.next = head; 最好是紧接着让head.next =null,因为第一个的next是null
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode p = reverseList(head.next);
head.next.next = head;
head.next = null;
return p;
}
}
执行时间:1ms,内存消耗:35.4MB
javaScript答案:
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
1. 使用循环头插法
var reverseList = function(head) {
var root = null;
var currentNode =head;
while(currentNode!==null){
const next = currentNode.next;
currentNode.next = root;
root = currentNode;
currentNode = next;
}
return root;
};
执行时间:92ms,内存消耗:34.9MB
2. 使用递归:
var reverseList = function(head) {
if(head==null || head.next==null){
return head;
}
let h = reverseList(head.next);
head.next.next = head;
head.next = null;
return h;
};
执行时间是92ms,内存消耗:35.7MB
2. 删除链表中的节点
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
现有一个链表 – head = [4,5,1,9],它可以表示为:
示例 1:
输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:
输入: head = [4,5,1,9], node = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
说明:
链表至少包含两个节点。
链表中所有节点的值都是唯一的。
给定的节点为非末尾节点并且一定是链表中的一个有效节点。
不要从你的函数中返回任何结果。
java答案:
1. 这个题没有告诉链表,所以不能使用平时的删除操作,前一节点的指针指向下一下节点。
class Solution {
public void deleteNode(ListNode node) {
if(node==null||node.next==null){
return;
}
else{
ListNode temp=node.next;
node.val=temp.val;
node.next=temp.next;
temp=null;
}
}
}
2. 因为题目中给出了node不是末尾的节点
class Solution {
public void deleteNode(ListNode node) {
node.val = node.next.val;
node.next = node.next.next;
}
}
JavaScript答案:
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} node
* @return {void} Do not return anything, modify node in-place instead.
*/
var deleteNode = function(node) {
node.val = node.next.val;
node.next =node.next.next;
};
3. 回文链表
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
两种方法 :1是栈,而是快慢指针,找到中间位置,然后要么反转后边的链表部分比较,要么从中间向两边比较。
java答案:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
1. 使用栈的先进后出特点,比较值是否相等
class Solution {
public boolean isPalindrome(ListNode head) {
if(head==null || head.next==null){
return true;
}
ListNode p = head;
Stack<ListNode> s = new Stack<>();
while(p!=null){
s.push(p);
p = p.next;
}
while(!s.isEmpty()){
if(s.peek().val != head.val){
return false;
}
s.pop();
head=head.next;
}
return true;
}
}
执行时间:12ms,内存消耗:45.1MB
2.
/*
* @lc app=leetcode.cn id=234 lang=java
*
* [234] 回文链表
*/
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
// 排除异常情况: 链表为空或只有一位时 返回真
if (head == null || head.next == null) {
return true;
}
// 排除异常情况: 链表只有两位时 直接判断这两位是否相等
if (head.next.next == null) {
return head.val == head.next.val;
}
// 声明快慢指针
ListNode slow = head;
ListNode fast = head;
// 声明last指针 用以记录循环的慢指针上一次遍历的节点
// 在遍历时转向前半部分链表
ListNode last = null;
// 用以记录遍历过程的临时值
ListNode tmp = null;
while (fast.next != null && fast.next.next != null) {
// 记录下当前遍历的节点 等下赋值给last指针
tmp = slow;
slow = slow.next;
fast = fast.next.next;
// 反转链表
if (last == null) {
// 第一次循环 last指针指向头部 尾部置空(变头为尾)
last = head;
last.next = null;
} else {
// 后续循环 将last指针赋值给当前遍历节点的下一位
tmp.next = last;
// last指针更新为当前遍历节点
last = tmp;
}
}
// 上述遍历结束 找到中间值 fast指针使命结束
// 接下来重新定义快慢指针使命
// 慢指针: 往前遍历; 快指针: 往后遍历
if (fast.next == null) {
// 如果是奇数个 slow此时位于中间数位置
// fast需要向后一位
// slow需要向前一位 即当前tmp值
fast = slow.next;
slow = tmp;
} else {
// 如果是偶数个 slow此时位于前半部分最后一位数位置
// fast需要向后一位
// slow不需要移动 但是需要补充指针转向操作
fast = slow.next;
slow.next = tmp;
}
// 遍历判断
while (slow != null || fast != null) {
if (slow == null || fast == null
|| slow.val != fast.val) {
return false;
}
slow = slow.next;
fast = fast.next;
}
return true;
}
}
执行时间:2ms,内存消耗:44.7MB
JavaScript答案:
1. 只要把上面第2种变量类型改成let即可。
执行时间是104ms,内存消耗是38.8MB
2. /**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var isPalindrome = function(head) {
if(!head) return true;
let s = head;
let f = head;
while(f && f.next && f.next.next) {
s = s.next;
f = f.next.next;
}
let right = s.next;
s.next = null;
let current = right;
let last = null;
//后半部分链表反转
while(current){
const next = current.next;
current.next = last;
last = current;
current = next;
}
a = head;
b = last;
while(a && b) {
if(a.val !== b.val) {
return false;
}
a = a.next;
b = b.next;
}
return true;
};
执行时间是92ms,内存消耗是39.4MB
4. 旋转链表
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:
输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL
java答案:
1. 先算出链表的长度,判断和k的关系,使用一个链表,找到右移k个之后的头节点,然后把尾部连到head上
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if(head==null || head.next==null||k==0)
return head;
ListNode p = head;
int count=0;
while(p!=null){
count++;
p=p.next;
}
//System.out.println(count);
if(count<=k){
k = k%count;
}
if(k==0){
return head;
}
// System.out.println("k"+k);
p=head;
k=count-k;
// System.out.println("k1:"+k);
while(k!=1){
p=p.next;
k--;
}
ListNode q =p.next;
ListNode m =p.next;
p.next=null;
while(q.next!=null){
q=q.next;
}
// System.out.println("q"+q.val);
q.next=head;
return m;
}
}
JavaScript答案:
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @param {number} k
* @return {ListNode}
*/
var rotateRight = function(head, k) {
if(head==null || head.next==null||k==0)
return head;
var p = head;
var count=0;
while(p!=null){
count++;
p=p.next;
}
if(count<=k){
k = k%count;
}
if(k==0){
return head;
}
p=head;
k=count-k;
while(k!=1){
p=p.next;
k--;
}
var q =p.next;
var m =p.next;
p.next=null;
while(q.next!=null){
q=q.next;
}
q.next=head;
return m;
};
5. 合并两个有序链表
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
两种方法 :1.循环迭代,2递归
Java答案:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
1.循环迭代:
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null){
return l2;
}
if(l2==null){
return l1;
}
ListNode p = l1;
ListNode q = l2;
ListNode l3 = null;
ListNode m = null;
while(p!=null && q!=null){
if(p.val<=q.val){
if(l3==null){
l3=p;
m=p;
p=p.next;
}else{
l3.next = p;
p=p.next;
l3=l3.next;
}
}else{
if(l3==null){
l3=q;
m=q;
q=q.next;
}else{
l3.next = q;
q=q.next;
l3=l3.next;
}
}
}
if(p!=null){
l3.next = p;
}
if(q!=null){
l3.next=q;
}
return m;
}
}
执行时间是2ms,内存消耗是35.6MB
时间复杂度是o(m+n),空间复杂度是o(1)
2. 递归
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null){
return l2;
}
if(l2==null){
return l1;
}
if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
}else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
执行时间是2ms,内存消耗是35.1MB
时间复杂度是o(m+n),空间复杂度:o(m+n):m+n个栈帧会消耗o(m+n)的空间。
JavaScript答案:
1. 上面1方法执行时间92ms,内存消耗是35.3MB
2. 上面2方法执行时间100ms,内存消耗是34.7MB
6. 合并k个排序链表
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
Java答案:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
1. 每次合并两个,调用之前写的两链表合并的代码,直到lists中只剩1个链表,并返回这个链表。
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if (lists.length == 0)
return null;
int k = lists.length;
while (k > 1) {
for (int i = 0; i < k / 2; i++)
lists[i] = mergeTwoLists(lists[i], lists[i + (k + 1) / 2]); //在中间分开,每次左边数组的第一个链表和右边数组的第一个链表合并
k = (k + 1) / 2; //k+1是因为可能存在奇数,多出来一个单的
}
return lists[0];
}
//两个链表合并
private ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode root = new ListNode(0);
ListNode node = root;
while (list1 != null && list2 != null) {
if (list1.val <= list2.val) {
node.next = list1;
list1 = list1.next;
} else {
node.next = list2;
list2 = list2.next;
}
node = node.next;
}
if (list1 == null)
node.next = list2;
if (list2 == null)
node.next = list1;
return root.next;
}
}
执行时间是5ms,内存消耗是39.6MB
2.逐一比较,初始化最小值是Integer中最大的值,查找最小的值,找到接在最终有序链表的后面,如果最后所有链表都为null时,返回。
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
ListNode sumAll = new ListNode(0); //最终有序链表的表头
ListNode p = sumAll;
boolean flag = true; //为了控制while循环的
while(flag){
int min = Integer.MAX_VALUE; // 写在while里是因为每次找到最小值以后,需要重新设置min为 Integer.MAX_VALUE
int idx = -1;
for(int i=0;i<lists.length;i++){
if(lists[i]!=null && lists[i].val < min){ //需要先判断lists[i]是否为空,否则报错
min = lists[i].val;
idx = i;
}
}
if(idx!=-1){ //找到最小值,接在最终链表后面
p.next = lists[idx];
p=p.next;
lists[idx] = lists[idx].next;
}else{ //如果idx是-1,代表所有链表为空了,结束while循环
flag = false;
}
}
return sumAll.next;
}
}
执行时间是334ms,内存消耗是40.7MB
3. 优先级队列
/**
* 优先级队列
* @param lists
* @return
*/
class Solution{
public ListNode mergeKLists(ListNode[] lists) {
if(lists == null || lists.length == 0){
return null;
}
PriorityQueue<ListNode> queue = new PriorityQueue<ListNode>(new Comparator<ListNode>() {
@Override
public int compare(ListNode o1, ListNode o2) {
return o1.val - o2.val;
}
});
for(int i = 0; i < lists.length; i++){
if(lists[i] != null){
queue.offer(lists[i]);
}
}
ListNode head = null;
ListNode cur = null;
while (!queue.isEmpty()){
ListNode node = queue.poll();
if(head == null){
head = node;
cur = head;
}else {
cur.next = node;
cur = cur.next;
}
if(node.next != null){
queue.offer(node.next);
}
}
return head;
}
}
执行时间是14ms,内存消耗是42.7MB
JavaScript答案:
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode[]} lists
* @return {ListNode}
*/
1. 用的上面两两归并的方法,但是注意整数相除
var mergeKLists = function(lists) {
if(lists.length==0){
return null;
}
let k = lists.length;
while(k>1){
for(let i=0;i<Math.floor(k/2);i++){
lists[i] = merget(lists[i],lists[i+Math.floor((k+1)/2)]);
}
k = Math.floor((k+1)/2);
}
return lists[0];
};
var merget = function(list1,list2){
if(list1==null){
return list2;
}
if(list2==null){
return list1;
}
let root = new ListNode(0);
let node = root;
while (list1 != null && list2 != null) {
if (list1.val <= list2.val) {
node.next = list1;
list1 = list1.next;
} else {
node.next = list2;
list2 = list2.next;
}
node = node.next;
}
if (list1 == null)
node.next = list2;
if (list2 == null)
node.next = list1;
return root.next;
};
执行时间是128ms,内存消耗是39.1MB