[路飞]_夜深人静来几个算法助兴

455 阅读4分钟

「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战剑指 Offer II 024. 反转链表

思路1:递归法

var reverseList = function(head) {
    if(head === null || head.next === null) return head;
    let left = head.next;
    const t = reverseList(head.next);
    head.next = left.next;
    left.next = head;
    return t

};

思路2:迭代法
这个思路很好理解,使用3个指针;头指针pre,当前指针curr,下一节点指针next;

将当前指针curr.next 指向pre;
将头指针指向当前指针;
将当前指针使命完成,让当前指针指向下一指针;
下一指针使命完成,下一节点指针next应该指向下下一节点;判断一下节点next是否为null,null是没有下下一节点的


var reverseList = function(head) {
   if(head === null) return head;
   let pre = null;
   let curr = head;
   let next = head.next;
   while(curr){
       curr.next = pre;
       pre = curr;
       curr = next;
       next = next === null?null:next.next
   }
   return pre

};

703. 数据流中的第 K 大元素

思路1
根据最大K元素很容易想到排序;将add的数据添加到数组,在添加过程中,通过查找方法将val数值放到数组对应位置,使添加完数据的数组依然是有序的;输出第K个元素即可得到答案;
代码如下:


var KthLargest = function(k, nums) {
    nums.sort((a,b)=>b-a)
    this.list = nums
    this.idx = k

};
KthLargest.prototype.add = function(val) {
    const idx = this.idx;
    const len = this.list.length;
    for(let i = 0 ; i < len ; i++){
        if(this.list[i] <= val){
            this.list.splice(i,0,val);
            break;
        }
    }
    if(this.list.length === len){
        this.list.push(val) 
    }
   
    return this.list[idx-1]
};

思路2 通过一上代码,你已经AC本题;但是进一步思考;既然只需要知道第K个最大值;哪在上述代码中是不是可以优化一下?
既然只需要获取第K个最大值,哪我排序的数组长度是不是只需要K;

通过一上代码,进一步优化代码如下:

var KthLargest = function(k, nums) {
    nums.sort((a,b)=>b-a);
    // 仅仅截取前K个数字
    this.list = nums.slice(0,k+1)
    this.idx = k

};


KthLargest.prototype.add = function(val) {
    const idx = this.idx;
    const len = this.list.length;
    for(let i = 0 ; i < len ; i++){
        if(this.list[i] <= val){
            this.list.splice(i,0,val);
            break;
        }
    }
    if(this.list.length === len){
        this.list.push(val)
    }
    
    //超出K个元素,删除数组中最后一个
    if(this.list.length === idx){
        this.list = this.list.slice(0,idx)
    }

    return this.list[idx-1]
};


面试题 02.07. 链表相交

思路:?不能说毫无思路,只能说没有头绪,手动🤪。
首先我是这么考虑的;
遍历一遍链表A,在A中添加标识数据,比如A.index = "A";
再遍历链表B,在B中找到index;第一个index有值的节点就是相交节点;
这思路绝对能找出相交节点;
奈何题目着重要求保持其原始结构 凉凉~!

在想!!!

思路2

假设: 链表A=[3,4,5,6];
链表B=[5,6]; 通过【尾部对齐】法将链表A、B变化为:
A = [5,6];
B = [5,6];
这不就可以了?

按照这个思路,获取A、B链表长度,将链表长度较长的链表通过while将链表与较短链表长度一致;然后再对比两链表节点;如果节点相同,则两链表相交,否则链表不相交;
根据上述思路书写代码如下,但是这段代码明显有可优化的空间;
时间复杂度分析O(max(A.length,B.length))


var getIntersectionNode = function(headA, headB) {
    let l1 = 0;
    let headerA = headA;
    let headerB = headB;
    while(headerA){
        l1++;
        headerA = headerA.next;
    }
    let l2 = 0;
   
    while(headerB){
        l2++;
        headerB = headerB.next;
    }
    // 如果两链表长度相同
    if(l1 === l2){
        headerA = headA;
        headerB = headB;
        while(headerA && headerB){
            if(headerA === headerB) return headerB;
            headerA  = headerA.next;
            headerB  = headerB.next;
        }
        return null
    }else if(l1 < l2){
        headerA = headA;
        headerB = headB;
        while(l1 < l2){
            headerB = headerB.next
            l2--;
        }
        while(headerA && headerB){
            if(headerA === headerB) return headerB;
            headerA  = headerA.next;
            headerB  = headerB.next;
        }
         return null
    }else{
        headerA = headA;
        headerB = headB;
        while(l2 < l1){
            headerA = headerA.next
            l1--;
        }
        while(headerA && headerB){
            if(headerA === headerB) return headerB;
            headerA  = headerA.next;
            headerB  = headerB.next;
        }
         return null
    }
    
};

思路2:

脑子实在想不出来,找了支笔写写画画,画出来个这; image.png 得出来个核心:a+b = b+a;😄; 先假设如果两个链表相交,且相交点为c;

那么就可以设:
链表A表头到交点长度为a,交点c到链表A位长度为c; 链表B表头到交点长度为b,交点c到链表B位长度为c;

那么链表A、链表B同时向前走一定有:

链表A向前长度a到交点c,在向前走长度c到达链表A尾,在向前走长度b又回到了交点c;

同样,链表B向前长度b到交点c,在向前走长度c到达链表B尾,在向前走长度a回到交点c;

结论:a + c + b = b + c + a;

根据这个思路:写代码如下:

var getIntersectionNode = function(headA, headB) {
    let node1 = headA;
    let node2 = headB;
    while(node1 !== node2){
        //如果二者不相同,两链表同时向前步进;
        node1 = node1 === null?headB:node1.next;
        node2 = node2 === null?headA:node2.next
    }
    //如果相同,返回链表
    return node1
    
};

看看这代码量,看看这思路;羡慕不羡慕

61. 旋转链表

这题对目前我的水品还是有点吃力的;还好,我知道如何旋转JavaScript数组的旋转,我准备参照这个思路,实现这个链表反转;


var rotateRight = function(head, k) {
  let len = 0;
  let header = head;
  //获取链表长度,因为旋转移动的K值可能超过链表长度
  while(header){
    len++;
    header = header.next
  }
  //超过链表长度 取余数即可
  let l = k%len;
  
  // 余数为0 表示链表与旋转长度一致;返回原链表
  if (!head || l == 0) return head;
  
  
  //假设链表长度为l;将链表按照k分为左侧部分与右侧部分,左侧部分长度为l-k;右侧部分长度为k;
  // 将左右部分互换即可实现链表旋转
  //通过快慢指针得到左右部分;
  
  //
  let fase = head;
  while(l){
    fase = fase.next;
    l--;
  }
  let slow = head;
  let last = 0;
  while(fase && slow){
    fase = fase.next;
    slow = slow.next;
    last++;

  }
  
 let resule = slow;
 while(resule && resule.next){
     resule = resule.next
 }
 // 将左侧链表放在右侧链表中
  while(last){
      resule.next = new ListNode(head.val);
      head = head.next;
      resule = resule.next;
      last--;
  }
  
  //返回左侧链表表头
   return slow


};

反转二叉树

这题第一个就想到递归

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