[路飞]_js算法实现:反转链表 旋转链表 链表相交 数据流中的第 K 大元素 翻转二叉树

394 阅读3分钟
 // 定义单链表的节点.
 function ListNode(val, next) {
      this.val = (val===undefined ? 0 : val)
      this.next = (next===undefined ? null : next)
 }

反转链表

问题: 给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。(leetcode 024)

思路: 定义三个指针,分别指向前一个结点,待反转头节点,待反转头节点原下一个节点

解法一:
var reverseList = function(head) {
if(head==null||head.next==null) return head;
let pre=null;
let cur=head;
let next=head.next;
while(cur){
    cur.next=pre;
    pre=cur;
    (cur=next)&&(next=next.next)
}
return pre;
};

解法二:递归
var reverseList=function(head){
if(head==null||head.next==null) return head;
let tail=head.next;
let p=reverseList(head.next);
head.next=tail.next;
tail.next=head;
return p
}

旋转链表

问题: 给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。(leetcode 61)

解法一:找到尾节点,连到首节点,并记录链表长度n。从头节点开始,找到n-n%k位置,断开向后的指向。
var rotateRight = function(head, k) {
    if(head==null||head.next==null)return head;
    let p=head;
    let n=1;
    while(p.next){
        p=p.next;
        n++;
    }
    p.next=head;
    k%=n;
    k=n-k;
    while(k){
        p=p.next;
        k--
    }
    head=p.next;
    p.next=null;
    return head
};

链表相交

问题: 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。(leetcode 02.07)

思路: 分别循环两个链表并比较,若其中一个链表的下一个节点为空,说明该链表短,则将临时指针指向长链表的头节点。如果有交点,那么在互相切换指针以后,就会在交点集合;否则都会指向对方的尾节点的下一个节点null.(就是想办法让他们走一样长度就可以了)

var getIntersectionNode = function(headA, headB) {
    let pA=headA;
    let pB=headB;
    while(pA!==pB){
        pA=pA===null?headB:pA.next;
        pB=pB===null?headA:pB.next;
    }
    return pA;
};

数据流中的第 K 大元素

问题: 设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。(leetcode 703)

请实现 KthLargest 类:

  • KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
  • int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。

思路: 用优先队列 建立一个大小为 k 的优先队列来存储前 k 大的元素,优先队列已排好序。

在单次插入的操作中,我们首先将元素 val 加入到优先队列中。如果此时优先队列的大小大于 k,我们需要将优先队列的队头元素弹出,以保证优先队列的大小为 k。

class KthLargest{
  constructor(k,nums){
  this.k = k;
  this.heap = new MinHeap();
  for (const x of nums) {
        this.add(x);
     }
  }
  add(){
    this.heap.offer(val);
    if (this.heap.size() > this.k) {
        this.heap.poll();
     }
     return this.heap.peek();
    }
}
class MinHeap {
    constructor(data = []) {
        this.data = data;
        this.comparator = (a, b) => a - b;
        this.heapify();
    }
    heapify() {
        if (this.size() < 2) return;
        for (let i = 1; i < this.size(); i++) {
        this.bubbleUp(i);
        }
    }
    peek() {
        if (this.size() === 0) return null;
        return this.data[0];
    }
    offer(value) {
        this.data.push(value);
        this.bubbleUp(this.size() - 1);
    }
    poll() {
        if (this.size() === 0) {
            return null;
        }
        const result = this.data[0];
        const last = this.data.pop();
        if (this.size() !== 0) {
            this.data[0] = last;
            this.bubbleDown(0);
        }
        return result;
    }
    bubbleUp(index) {
        while (index > 0) {
            const parentIndex = (index - 1) >> 1;
            if (this.comparator(this.data[index], this.data[parentIndex]) < 0) {
                this.swap(index, parentIndex);
                index = parentIndex;
            } else {
                break;
            }
        }
    }
    bubbleDown(index) {
        const lastIndex = this.size() - 1;
        while (true) {
            const leftIndex = index * 2 + 1;
            const rightIndex = index * 2 + 2;
            let findIndex = index;
            if (
                leftIndex <= lastIndex &&
                this.comparator(this.data[leftIndex], this.data[findIndex]) < 0
            ) {
                findIndex = leftIndex;
            }
            if (
                rightIndex <= lastIndex &&
                this.comparator(this.data[rightIndex], this.data[findIndex]) < 0
            ) {
                findIndex = rightIndex;
            }
            if (index !== findIndex) {
                this.swap(index, findIndex);
                index = findIndex;
            } else {
                break;
            }
        }
    }
 size() {
        return this.data.length;
    }
  swap(index1, index2) {
        [this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]];
    }
}

翻转二叉树

翻转一棵二叉树。(leetcode 226)

思路: 递归 先翻转叶子节点 然后翻转向上的叶子节点树。

var invertTree = function(root) {
    if (root === null) {
        return null;
    }
    const left = invertTree(root.left);
    const right = invertTree(root.right);
    root.left = right;
    root.right = left;
    return root;
};