记录一些很主观的东西,有些想法归根结底可能还是自己的实力不够。
与链表有关的算法题,一般需要进行以下考虑:
1.区分所操作节点的类型:首结点、中间节点、尾节点。
2.如果需要对头结点进行相关操作,那么考虑是否要添加虚拟头结点。
3.如果一个问题涉及到数据的顺序和要进行的操作相逆的话,考虑使用栈结构转存。而且使用数组栈效率高一点。
https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
思路描述
代码实现
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode a = headA;
ListNode b = headB;
while (a != b) {
a = a != null ? a.next : headB;
b = b != null ? b.next : headA;
}
return a;
}
}
注意事项
拓展延伸
https://leetcode-cn.com/problems/reverse-linked-list/
思路描述
代码实现
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null) return head;
ListNode newhead, temp, node;
//取下需要逆转的链表的第一个,作为新链表的结尾
newhead = head;
head = head.next;
//结尾置空
newhead.next = null;
while(head != null){
node = head;//先记录下当前需要操作的节点
temp = head.next;//记录下下一个要操作的节点
node.next = newhead;//当前操作节点接到逆转后的链表的头部
newhead = node;//当前操作节点成为逆转后链表的新头
head = temp;//未逆转链表的新头
}
return newhead;
}
}
注意事项
拓展延伸
递归的解法:
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode next = head.next;
ListNode newHead = reverseList(next);
next.next = head;
head.next = null;
return newHead;
}
https://leetcode-cn.com/problems/merge-two-sorted-lists/
思路描述
woshishabi.
代码实现
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null) return l2;
if(l2 == null) return l1;
ListNode tempNode, newhead, newtail;
if(l1.val <= l2.val){
newhead = l1;
newtail = l1;
l1 = l1.next;
}else{
newhead = l2;
newtail = l2;
l2 = l2.next;
}
while(l1 != null && l2 != null){
while((l1 != null && l2 != null) && l1.val <= l2.val){
newtail.next = l1;
newtail = l1;
l1 = l1.next;
}
while((l1 != null && l2 != null) && l2.val <= l1.val){
newtail.next = l2;
newtail = l2;
l2 = l2.next;
}
}
newtail.next = (l2 == null)? l1 : l2;
return newhead;
}
}
注意事项
拓展延伸
递归的解法:
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;
}
}
https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/
思路描述
这也算是双指针吧,一快一慢,如果快指针的val和慢指针的val相同,快指针就向后移。直到链表结束。
代码实现
java代码:
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head.next == null) return head;
ListNode l1 = head, l2 = head;
while(l2 != null){
while(l2 != null && l1.val == l2.val){
l2 = l2.next;
}
l1.next = l2;
l1 = l2;
}
return head;
}
}
注意事项
拓展延伸
递归的解法:
public ListNode deleteDuplicates(ListNode head) {
if (head == null || head.next == null) return head;
head.next = deleteDuplicates(head.next);
return head.val == head.next.val ? head.next : head;
}
https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
思路描述
代码实现
java代码:
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
// 前置处理
if(head == null || n == 0) return head;
else if(head.next == null){
head = null;
return head;
}
// 确定prior和tail之间的距离
ListNode prior = head, tail = head;
do{
tail = tail.next;
n--;
}while(n != 0);
// 需要删除的是头结点
if(tail == null){
return head.next;
}
// 否则,双指针同步后移,遍历列表
while(tail.next != null){
prior = prior.next;
tail = tail.next;
}
// 删除节点
prior.next = prior.next.next;
return head;
}
}
注意事项
拓展延伸
https://leetcode-cn.com/problems/swap-nodes-in-pairs/
思路描述
代码实现
java代码:
class Solution {
public ListNode swapPairs(ListNode head) {
if(head == null || head.next == null) return head;
ListNode swap = head, temp = head.next.next;
head = head.next;
head.next = swap;
swap.next = swapPairs(temp);
return head;
}
}
思路描述
代码实现
java代码:
class Solution {
public ListNode swapPairs(ListNode head) {
//
if(head == null || head.next == null){
return head;
}
if(head.next.next == null){
ListNode swap = head.next;
swap.next = head;
head.next = null;
return swap;
}
if(head.next.next.next == null){
ListNode swap = head.next, tail = head.next.next;
swap.next = head;
head.next = tail;
return swap;
}
ListNode p1, p2, p4, newhead = head.next, swap;
while(head.next.next.next != null){
p1 = head;
p2 = head.next;
p4 = head.next.next.next;
head = head.next.next;
swap = p2;
swap.next = p1;
p1.next = p4;
if(head == null || head.next == null){
return newhead;
}
if(head.next.next == null){
swap = head.next;
swap.next = head;
head.next = null;
return newhead;
}
if(head.next.next.next == null){
swap = head.next;
ListNode tail = head.next.next;
swap.next = head;
head.next = tail;
return newhead;
}
}
return newhead;
}
}
注意事项
拓展延伸
class Solution {
public ListNode swapPairs(ListNode head) {
// 设置一个链表头结点的前置节点
ListNode node = new ListNode(-1);
node.next = head;
ListNode pre = node;
while (pre.next != null && pre.next.next != null) {
ListNode l1 = pre.next, l2 = pre.next.next;
ListNode next = l2.next;
l1.next = next;
l2.next = l1;
pre.next = l2;
pre = l1;
}
return node.next;
}
}
题目
思路描述
代码实现
java代码:
思路描述
代码实现
java代码:
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
// 引入栈结构
Stack<Integer> stack1 = listToStack(l1);
Stack<Integer> stack2 = listToStack(l2);
// 初始化进位
int carry = 0, a = 0, b = 0, result;
// 初始化返回的链表头结点
ListNode newhead = new ListNode(-1, null);
while(!stack1.isEmpty() || !stack2.isEmpty() || carry != 0){
a = stack1.isEmpty() ? 0 : stack1.pop();
b = stack2.isEmpty() ? 0 : stack2.pop();
result = a + b + carry;
carry = result / 10;
result = result % 10;
// 头插法建立链表,保证顺序
ListNode node = new ListNode(result);
node.next = newhead.next;
newhead.next = node;
}
return newhead.next;
}
// 私有方法将链表中的val存放到栈中
private Stack<Integer> listToStack(ListNode l){
Stack<Integer> stack = new Stack<>();
while(l != null){
stack.push(l.val);
l = l.next;
}
return stack;
}
}
注意事项
拓展延伸
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
// 引入数组栈结构
int stack1[] =new int[100], top1 = -1;
while(l1 != null){
stack1[++top1] = l1.val;
l1 = l1.next;
}
int stack2[] = new int[100], top2 = -1;
while(l2 != null){
stack2[++top2] = l2.val;
l2 = l2.next;
}
// 初始化进位
int carry = 0, a = 0, b = 0, result;
// 初始化返回的链表头结点
ListNode newhead = new ListNode(-1, null);
while(top1 != -1 || top2 != -1 || carry != 0){
a = (top1 == -1) ? 0 : stack1[top1--];
b = (top2 == -1) ? 0 : stack2[top2--];
result = a + b + carry;
carry = result / 10;
result = result % 10;
//System.out.println(top1);
// 头插法建立链表,保证顺序
ListNode node = new ListNode(result);
node.next = newhead.next;
newhead.next = node;
}
return newhead.next;
}
}
题目
思路描述
代码实现
java代码:
class Solution {
public boolean isPalindrome(ListNode head) {
if(head.next == null) return true;
//
ListNode fast = head, slow = head;
int i = 0;
Stack<Integer> stack = new Stack<>();
while(fast != null){
i++;
stack.push(slow.val);
// fast后移两个并进行判断链表是否结束
fast = fast.next;
if(fast == null) break;
else {
i++;
fast = fast.next;
}
if(fast == null) break;
// slow后移一个
slow = slow.next;
}
// 区分链表的长度
if(i % 2 == 0){// 偶数
slow = slow.next;
while(slow != null){
if(slow.val != stack.pop()){
return false;
}
slow = slow.next;
}
}else{// 奇数
i = stack.pop();
slow = slow.next;
while(slow != null){
if(slow.val != stack.pop()){
return false;
}
slow = slow.next;
}
}
return true;
}
}
思路描述
代码实现
java代码:
class Solution {
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null) return true;
//
ListNode fast = head.next, slow = head, temp;
ListNode newhead = new ListNode(-1, null);
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
//开始逆置后续链表
if(fast == null){// 链表长度为奇数
while(slow != null){
temp = slow.next;
slow.next = newhead.next;
newhead.next = slow;
slow = temp;
}
}else{// 链表长度为偶数
slow = slow.next;
while(slow != null){
temp = slow.next;
slow.next = newhead.next;
newhead.next = slow;
slow = temp;
}
}
// 判断元素是否相同
while(newhead.next != null){
if(head.val == newhead.next.val){
}else{
return false;
}
head = head.next;
newhead = newhead.next;
}
return true;
}
}
注意事项
拓展延伸
ListNode fast = head.next, slow = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
ListNode fast = head.next.next, slow = head;
while(fast != null && fast.next != null && fast.next.next != null){
slow = slow.next;
fast = fast.next.next.next;
}
题目
思路描述
代码实现
java代码:
class Solution {
public ListNode[] splitListToParts(ListNode head, int k) {
// 预处理
if(k == 1) {
ListNode[] array = new ListNode[k];
array[0] = head;
return array;
}
// 获取链表的长度
ListNode h = head;
int len = 0;
while(h != null){
len++;
h = h.next;
}
// 分割后链表最佳长度
int lenSplited = len / k;
// 链表平分后剩余数量
int mod = len % k;
int[] lensSplited = new int[k];
for(int i =0; i < k; i++){
lensSplited[i] = 0;
}
int i = 0;
// 判断链表是否足够长,用于划分
if(k > len){// 不够长
while(i < len){
lensSplited[i] = lenSplited;
i++;
}
return split(head, len, lensSplited, k);
}else{// 够长
while(i < k){
lensSplited[i] = lenSplited + ((mod-- > 0)? 1 : 0);
i++;
}
return split(head, len, lensSplited, k);
}
}
// 分割链表为指定数量指定长度
private ListNode[] split(ListNode head, int len, int[] lensSplited, int k){
ListNode[] array = new ListNode[k];
ListNode temp;
// 初始值设为null
for(int i = 0; i < k; i++){
array[i] = null;
}
int listNum = 0;
while(listNum < k && head != null){
array[listNum] = head;
int l = 1;
while(l < lensSplited[listNum]){
head = head.next;
l++;
}
if(head != null){
temp = head.next;
head.next = null;
head = temp;
}
listNum++;
}
return array;
}
}
注意事项
拓展延伸
这个更简洁。
class Solution {
public ListNode[] splitListToParts(ListNode head, int k) {
int N = 0;
ListNode cur = head;
while (cur != null) {
N++;
cur = cur.next;
}
int mod = N % k;
int size = N / k;
ListNode[] ret = new ListNode[k];
cur = head;
for (int i = 0; cur != null && i < k; i++) {
ret[i] = cur;
int curSize = size + (mod-- > 0 ? 1 : 0);
for (int j = 0; j < curSize - 1; j++) {
cur = cur.next;
}
ListNode next = cur.next;
cur.next = null;
cur = next;
}
return ret;
}
}
题目
思路描述
代码实现
java代码:
class Solution {
public ListNode oddEvenList(ListNode head) {
// 预处理
if(head == null || head.next == null || head.next.next == null){
return head;
}
// 头结点
ListNode oddHead = head, evenHead = head.next;
ListNode oddNode = evenHead.next, evenNode = oddNode.next, oddTail = oddHead, evenTail = evenHead;
// 遍历区分奇偶节点
while(oddNode != null && evenNode != null){
// 尾插法奇链表
oddTail.next = oddNode;
oddTail = oddNode;
oddNode = oddNode.next;
if(oddNode != null){
oddNode = oddNode.next;
}else{
break;
}
// 尾插法偶链表
evenTail.next = evenNode;
evenTail = evenNode;
evenNode = evenNode.next;
if(evenNode != null){
evenNode = evenNode.next;
}else{
break;
}
}
// 偶链表链到奇链表后
if(oddNode == null){
oddTail.next = evenHead;
evenTail.next = null;
}else{
oddTail.next = oddNode;
oddTail = oddNode;
oddTail.next = evenHead;
evenTail.next = null;
}
return oddHead;
}
}
java代码:
class Solution {
public ListNode oddEvenList(ListNode head) {
// 预处理
if(head == null || head.next == null || head.next.next == null){
return head;
}
//
ListNode evenHead = head.next, oddNode = head, evenNode = head.next;
while(evenNode != null && evenNode.next != null){
oddNode.next = oddNode.next.next;
oddNode = oddNode.next;
evenNode.next = evenNode.next.next;
evenNode = evenNode.next;
}
// 偶链表接到奇链表后面
oddNode.next = evenHead;
return head;
}
}
注意事项
拓展延伸
题目
思路描述
代码实现
java代码:
class Solution {
public Node copyRandomList(Node head) {
// 先按照普通的链表进行复制,random指针先赋值为null
Node newH = new Node(-1001); // 头指针尾指针复制链表
Node tail = new Node(-1001);
Node originalHead = head;
newH = tail;
int len = 0; // 获取链表的长度
while (head != null) { // 仅复制next节点
Node tmp = new Node(-1001);
tmp.val = head.val;
tmp.next = head.next;
// tail.random = null; // 初始化的tail的random本身就是空
head = head.next;
tail.next = tmp;
tail = tmp;
len++;
}
tail.next = null; // 尾节点置空
head = originalHead; // 原始链表的头,用于遍历
Node copyListHead = newH.next; // 复制链表的头
Node nowProcess = copyListHead; // 当前正在处理的复制链表中的节点
Node tmpHead = new Node(-1001); // 复制链表的头,用于遍历复制链表,寻找对应的random节点
int copyIdx;
while (head != null) {
// 首先找到当前head节点指向的random节点是链表中的倒数第几个
// 倒数第零个表示null
int idx= 0; // 计数
Node randNode = head.random;
while (randNode != null) {
idx++;
randNode = randNode.next;
}
// 同样找到在copyList中对应的倒数那个
// 这里可以优化一下
copyIdx = len;
tmpHead = copyListHead;
while (copyIdx != idx) {
tmpHead = tmpHead.next;
copyIdx--;
}
nowProcess.random = tmpHead;
head = head.next;
nowProcess = nowProcess.next;
}
return newH.next;
}
}
思路描述
class Solution {
public Node copyRandomList(Node head) {
// 1 -> 2 -> 3 -> 4
// 1 -> 1 -> 2 -> 2 -> 3 -> 3 -> 4 -> 4
if (head == null) return head; // 特殊情况
Node node = head;
while (node != null) { // 将原始链表复制一份,并穿插在原始链表中,每两个相同的相邻接点中第二个是新节点,未来用于返回
Node newNode = new Node(node.val); // 新建节点
newNode.next = node.next; // 复制next关系
node.next = newNode; // 接在原始节点后面
node = newNode.next; // 遍历
}
Node tmpHead = head;
while (tmpHead != null) {
node = tmpHead.next;
node.random = tmpHead.random == null ? null : tmpHead.random.next; // 查找random节点,并赋值给copy出来的链表
tmpHead = tmpHead.next.next;
}
// 将原始链表的next关系复原,并修改所复制链表的next关系。
tmpHead = head;
Node copyHead = head.next;
while (tmpHead != null) {
node = tmpHead.next;
tmpHead.next = tmpHead.next.next; // 原始链表next复原
node.next = node.next == null ? null : node.next.next; // 复制链表的next关系复原
tmpHead = tmpHead.next; // 遍历
}
return copyHead;
}
}
注意事项
拓展延伸
题目
思路描述
代码实现
class Solution {
int len;
public ListNode reverseKGroup(ListNode head, int k) {
// 统计长度 + 翻转链表
if (head.next == null) return head; // 特殊情况
ListNode h = new ListNode();
ListNode tail = new ListNode();
h.next = null;
tail = null;
ListNode[] headTail = new ListNode[2];
ListNode[] twoHead = new ListNode[2];
do {
len = k;
twoHead = countLength(head);
headTail = reverseLinkList(twoHead[0], twoHead[1]);
if (h.next == null) {
h.next = headTail[0];
}
if (tail == null) {
tail = headTail[1];
} else {
tail.next = headTail[0];
tail = headTail[1];
}
head = twoHead[1];
} while(head != null);
return h.next;
}
private ListNode[] countLength(ListNode head) {
ListNode[] twoHead = new ListNode[2];
twoHead[0] = head;
while (len-- != 0 && head != null) {
head = head.next;
}
twoHead[1] = head;
return twoHead;
}
private ListNode[] reverseLinkList(ListNode head, ListNode nextHead) {
if (nextHead == null && len != -1) { // 剩余长度不够的情况
return new ListNode[]{head, null};
}
// 翻转链表
ListNode h = new ListNode();
ListNode tail = new ListNode();
h.next = null;
tail = null;
ListNode tmp = new ListNode();
while (head != nextHead) {
tmp = head;
head = head.next;
tmp.next = h.next;
h.next = tmp;
if (tail == null) {
tail = tmp;
}
}
tail.next = nextHead;
return new ListNode[]{h.next, tail};
}
}
注意事项
拓展延伸
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode hair = new ListNode(0); // 指向head的“头发”结点,头之前是头发23333
hair.next = head;
ListNode pre = hair;
while (head != null) {
ListNode tail = pre;
// 查看剩余部分长度是否大于等于 k
for (int i = 0; i < k; ++i) {
tail = tail.next;
if (tail == null) { // 如果此时剩下的节点个数不够k个,那么就可以直接返回了
return hair.next;
}
}
ListNode nex = tail.next; // 找到了要反转的这一段的尾节点,并且求出下一段的头节点
ListNode[] reverse = myReverse(head, tail); // 将目前head 到 tail这一段进行反转
head = reverse[0]; // 反转后的头
tail = reverse[1]; // 反转后的尾
// 把子链表重新接回原链表
pre.next = head;
tail.next = nex;
// 更新头发和头指针,进行下一段的反转工作
pre = tail;
head = nex;
}
return hair.next;
}
public ListNode[] myReverse(ListNode head, ListNode tail) { // 反转具体的一段链表
ListNode prev = tail.next; // 先求出到哪里终止反转,tail的next
ListNode p = head; //
while (prev != tail) { // 这个反转的方式有点妙啊
ListNode nex = p.next; //
p.next = prev; //
prev = p;
p = nex;
}
return new ListNode[]{tail, head};
}
}
题目
思路描述
代码实现
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if (head == null || head.next == null) {
return head;
}
if (left >= right) {
return head;
}
ListNode h = new ListNode();
h.next = head;
ListNode lTail, mHead, mTail, rHead;
mHead = h;
while (left != 1) {
mHead = mHead.next;
left--;
right--;
}
lTail = mHead; // 被截断后的左侧链表的尾节点
mHead = mHead.next; // 中间这一段的head
mTail = mHead;
while (right != 1) {
mTail = mTail.next;
right--;
}
rHead = mTail.next; // 被截断后的右侧链表的head
mTail.next = null; // 截断中间这一段链表
ListNode[] headAndTail = new ListNode[2];
headAndTail = reverse(mHead); // 反转中间链表
lTail.next = headAndTail[0];
headAndTail[1].next = rHead;
return h.next;
}
private ListNode[] reverse(ListNode head) { // 反转链表
ListNode h = new ListNode();
h.next = null;
ListNode tmp, tail = head;
while (head != null) {
tmp = head;
head = head.next;
tmp.next = h.next;
h.next = tmp;
}
return new ListNode[]{h.next, tail};
}
}
注意事项
拓展延伸
题目
思路描述
代码实现
// 链表取中 + 反转 + 交错链接
class Solution {
public void reorderList(ListNode head) {
// 先找中间,分成两段
// 后面反转
// 交错链接
if (head == null || head.next == null) {
return;
}
ListNode fast = head.next, slow = head; // 注意快指针一定要先后置一个
while (fast != null && fast.next != null) { // 链表取中一定要这样写!!!
slow = slow.next;
fast = fast.next.next;
}
ListNode head2 = slow.next;
slow.next = null;
head2 = reverse(head2);
ListNode tmp1;
while (head2 != null) {
tmp1 = head;
head = head.next;
tmp1.next = head2;
tmp1 = head2;
head2 = head2.next;
tmp1.next = head;
}
return;
}
private ListNode reverse(ListNode head) {
ListNode hair = new ListNode();
hair.next = null; // 今天链表反转都写错了
ListNode tmp;
while (head != null) {
tmp = head;
head = head.next;
tmp.next = hair.next;
hair.next = tmp;
}
return hair.next;
}
}
注意事项
// 取中
ListNode fast = head.next, slow = head; // 注意快指针一定要先后置一个
while (fast != null && fast.next != null) { // 链表取中一定要这样写!!!
slow = slow.next;
fast = fast.next.next;
}
// 取三分之一
ListNode fast = head.next, slow = head; // 注意快指针一定要先后置一个
while (fast != null && fast.next != null && fast.next.next != null) { // 一定要这样写!!!
slow = slow.next;
fast = fast.next.next.next;
}
拓展延伸
题目
思路描述
代码实现
// 归并 时空复杂度O(knlogk), O(logk)
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
int n = lists.length;
if (n == 0) return null;
if (n == 1) return lists[0]; // 特殊情况处理
return merge(lists, 0, n - 1); // 划分子问题
}
private ListNode merge(ListNode[] lists, int l, int r) { // 切分子问题
if (l > r) {
return null; // 如果当前没有子问题可以切分,直接返回null
}
if (l == r) {
return lists[l]; // 如果当前的区间中只有一个链表,直接返回这个链表
}
int mid = l + ((r - l) >> 1); // 区间中有多个链表,我们取中平分
ListNode l1 = merge(lists, l, mid); // 递归的划分子问题
ListNode l2 = merge(lists, mid + 1, r);
return mergeSort(l1, l2); // 传统的归并过程
}
private ListNode mergeSort(ListNode l1, ListNode l2) {
if (l1 == null || l2 == null) {
return l1 == null ? l2 : l1;
}
ListNode hair = new ListNode();
ListNode tail = hair;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
tail.next = l1;
l1 = l1.next;
} else {
tail.next = l2;
l2 = l2.next;
}
tail = tail.next;
}
tail.next = l1 == null ? l2 : l1;
return hair.next;
}
}
// 优先队列,小顶堆 时空复杂度O(knlogk), O(k) 这里用了优先队列,优先队列中的元素不超过 k 个,故渐进空间复杂度为 O(k)。
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
int n = lists.length;
if (n == 0) return null;
PriorityQueue<ListNode> pq = new PriorityQueue<>(n, (a, b) -> (a.val - b.val)); // 自定义比较器
for (int i = 0; i < n; i++) { // 第一次放入
if (lists[i] != null) {
pq.offer(lists[i]);
}
}
ListNode hair = new ListNode();
ListNode tail = hair;
ListNode tmp;
while (!pq.isEmpty()) {
tmp = pq.poll();
tail.next = tmp;
tail = tail.next;
if (tmp.next != null) {
pq.offer(tmp.next);
}
}
return hair.next;
}
}
注意事项
拓展延伸
题目
思路描述
代码实现
// 归并 时空复杂度:O(nlogn) O(logn)
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) // 如果当前是空或者只有一个节点
return head;
ListNode fast = head.next, slow = head;
while (fast != null && fast.next != null) { // 分割子问题
slow = slow.next;
fast = fast.next.next;
}
ListNode tmp = slow.next; // tmp右侧链表的头
slow.next = null; // 分割链表
ListNode left = sortList(head); // 左右两个区间
ListNode right = sortList(tmp);
ListNode hair = new ListNode(0); // 合并后的头
ListNode tail = hair; // 尾插法
while (left != null && right != null) { // 归并
if (left.val < right.val) {
tail.next = left;
left = left.next;
} else {
tail.next = right;
right = right.next;
}
tail = tail.next;
}
tail.next = left != null ? left : right;
return hair.next;
}
}
// 快排 时空复杂度O(nlogn)O(logn)
class Solution {
public ListNode sortList(ListNode head) {
return quickSort(head);
}
public ListNode quickSort(ListNode head) {
if(head == null || head.next == null) return head; // 如果问题规模过小,直接返回
ListNode slow = head, fast = head.next; // 快慢指针取中间节点
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
int val = slow.val; // 基准值
ListNode h1 = new ListNode(); // 基准值左侧的链表头
ListNode h2 = new ListNode(); // 基准值右侧的链表头
ListNode h3 = new ListNode(); // 可能会有和基准值相同的节点,我们把他全部保留在这个头结点后面,这样我们可以减少接下来的子问题的规模
ListNode t1 = h1, t2 = h2; // 基准值左侧和右侧的链表的尾指针,因为我们是要使用尾插法保证顺序
ListNode t3 = h3; // 所有的基准值,为了保持一致,我们一样用尾插法构造链表
ListNode cur = head; // 当前遍历到的元素
while(cur != null) {
ListNode next = cur.next; // 暂存下一个元素
if(cur.val < val) { // 如果当前这个元素的大小小于基准值
cur.next = t1.next; // 使用尾插法将当前这个节点链到基准值左侧的链表上
t1.next = cur;
t1 = t1.next;
} else if(cur.val > val) { // 如果当前这个元素的大小大于基准值
cur.next = t2.next; // 使用尾插法将当前这个节点链到基准值右侧的链表上
t2.next = cur;
t2 = t2.next;
} else { // 相等的部分,留在这里,等着链接
cur.next = t3.next;
t3.next = cur;
t3 = t3.next;
}
cur = next; // 遍历下一个元素
}
h1 = quickSort(h1.next); // 递归左侧链表,注意这里的h1已经不是原来的h1了。
h2 = quickSort(h2.next); // 递归右侧链表
h3 = h3.next;
t3.next = h2; // 基准值链表链接右侧较大部分链表
if(h1 == null) { // 如果左侧链表为空,直接返回基准值以及基准值之后的元素
return h3;
} else { // 否则,我们需要找到左侧传回链表的尾部,因为原来左侧只是经过了划分,而没有排序,排完序后我们需要重新寻找它的尾节点
t1 = h1; // 寻找尾节点
while(t1.next != null) {
t1 = t1.next;
}
t1.next = h3; // 链接上基准值和基准值右侧的节点
return h1;
}
}
}
注意事项
拓展延伸
题目
思路描述
代码实现
public class LRUCache {
// 定义内部类,
class DLinkedNode {
int key;
int value;
DLinkedNode prev;
DLinkedNode next;
public DLinkedNode() {}
public DLinkedNode(int _key, int _value) {key = _key; value = _value;}
}
// 定义成员变量
private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>(); // hashmap用于get
private int size; // 当前大小
private int capacity; // 容量上限
private DLinkedNode head, tail; // 双向链表的头尾指针
public LRUCache(int capacity) { // 初始化
this.size = 0; // 初始容量为零
this.capacity = capacity; // 容量上限
// 使用伪头部和伪尾部节点
head = new DLinkedNode();
tail = new DLinkedNode();
head.next = tail; // 初始化指向
tail.prev = head;
}
public int get(int key) { // get 方法
DLinkedNode node = cache.get(key); // 先获取这个节点
if (node == null) { // 不存在的话返回 -1
return -1;
}
moveToHead(node); // 如果 key 存在,先通过哈希表定位,再移到头部
return node.value;
}
public void put(int key, int value) {
DLinkedNode node = cache.get(key); // 通过hashmap获取对应节点
if (node == null) {
DLinkedNode newNode = new DLinkedNode(key, value); // 如果 key 不存在,创建一个新的节点
cache.put(key, newNode); // 添加进哈希表
addToHead(newNode); // 添加一个新节点至双向链表的头部
++size; // 当前容量++
if (size > capacity) { // 如果超出容量,删除双向链表的尾部节点
DLinkedNode tail = removeTail();
cache.remove(tail.key); // 删除哈希表中对应的项
--size; // 容量--
}
} else {
node.value = value; // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
moveToHead(node); // 移动旧有节点到队头
}
}
// 双向链表的操作
private void addToHead(DLinkedNode node) { // 新添加进来的节点放到队头
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(DLinkedNode node) { // 删除一个节点
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void moveToHead(DLinkedNode node) { // 移动一个已存在的节点到队头,相当于把这个节点从原来的双向链表中删除,再新建一个节点到队头
removeNode(node);
addToHead(node);
}
private DLinkedNode removeTail() { // 删除最久没有使用的节点
DLinkedNode res = tail.prev; // 先获取队尾的节点
removeNode(res); // 删除这个节点
return res; // 返回被删除的节点,用于将hashmap中的节点一并删除
}
}
注意事项
拓展延伸