21.合并两个有序链表
var mergeTwoLists = function(list1, list2) {
if(list1==null) return list2
if(list2==null) return list1
let newHead = new ListNode(0,null) //创建一个虚拟节点
let cur = newHead
let cur1 = list1,cur2 = list2
while(cur1&&cur2){
if(cur1.val<=cur2.val){
cur.next = cur1
cur = cur1
cur1 = cur1.next
}else{
cur.next = cur2
cur = cur2
cur2 = cur2.next
}
}
while(cur1){
cur.next = cur1
cur = cur1
cur1 = cur1.next
}
while(cur2){
cur.next = cur2
cur = cur2
cur2 = cur2.next
}
return newHead.next
};
203.移除链表元素
方法一:新建虚拟节点+pre
var removeElements = function(head, val) {
if(head==null) return null
let newHead = new ListNode(-1)
newHead.next = head
let cur = head,pre = newHead
while(cur){
if(cur.val==val){
cur = cur.next
pre.next = cur
}else{
pre = cur
cur = cur.next
}
}
return newHead.next
};
方法二:参考大佬的写法
var removeElements = function(head, val) {
//设置一个虚拟头节点
let newHead = new ListNode(0,head)
let cur = newHead
while(cur.next){
if(cur.next.val==val){
cur.next = cur.next.next
continue
}
cur = cur.next
}
return newHead.next
};
707.设计链表
//节点的构造函数
class ListNode {
constructor(val,next){
this.val = val
this.next = next
}
}
var MyLinkedList = function() {
//链表的初始化
this.size = 0
this.head = null
this.tail = null
};
/**
* @param {number} index
* @return {number}
*/
//用来获取某个索引的结点
MyLinkedList.prototype.getNode = function(index){
if(index<0||index>=this.size) return null
let cur = new ListNode(0,this.head) //创建一个虚拟节点
while(index-- >=0){
cur = cur.next
}
return cur
}
//获取某个index的值
MyLinkedList.prototype.get = function(index) {
if(index<0||index>=this.size) return -1
return this.getNode(index).val
};
/**
* @param {number} val
* @return {void}
*/
MyLinkedList.prototype.addAtHead = function(val) {
let newHead = new ListNode(val,this.head)
this.head = newHead
//判断尾巴结点
if(!this.tail){
this.tail = this.head
}
this.size++
};
/**
* @param {number} val
* @return {void}
*/
MyLinkedList.prototype.addAtTail = function(val) {
let newTail = new ListNode(val,null)
this.size++
if(this.tail){
this.tail.next = newTail
this.tail = newTail
return
}
this.head = newTail
this.tail = newTail
};
/**
* @param {number} index
* @param {number} val
* @return {void}
*/
MyLinkedList.prototype.addAtIndex = function(index, val) {
if(index>this.size) return
if(index<=0){
this.addAtHead(val)
return
}
if(index==this.size){
this.addAtTail(val)
return
}
let preNode = this.getNode(index-1)
preNode.next = new ListNode(val,preNode.next)
this.size++
};
/**
* @param {number} index
* @return {void}
*/
MyLinkedList.prototype.deleteAtIndex = function(index) {
if(index<0||index>=this.size) return //这里已经排除空的情况了
if(index==0){
this.head = this.head.next
//处理尾巴结点
if(index==this.size-1){
this.tail = this.head
}
this.size--
return
}
//获取上一个结点
let preNode = this.getNode(index-1)
preNode.next = preNode.next.next
if(index==this.size-1){
this.tail = preNode
}
this.size--
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* var obj = new MyLinkedList()
* var param_1 = obj.get(index)
* obj.addAtHead(val)
* obj.addAtTail(val)
* obj.addAtIndex(index,val)
* obj.deleteAtIndex(index)
*/
206.反转链表
方法一:头插法
var reverseList = function(head) {
//头插法
if(head==null||head.next==null) return head
//创建一个虚拟节点
let newHead = new ListNode(-1,null)
let cur = head
let nextNode = null
while(cur){
//保留cur的下一个节点
nextNode = cur.next
cur.next = newHead.next
newHead.next = cur
//然后找回nextNode
cur = nextNode
}
return newHead.next
};
递归法一
var reverseList = function(head) {
//这个递归式从后往前进行翻转
const helper = curNode =>{
//递归跳出的条件 要考虑空链表的情况所以要加上curNode==null这一判断条件
if(curNode==null||curNode.next==null) return curNode
//p相当于是最后一个结点
let p = helper(curNode.next)
// if(p!==null){ 这里的if语句可以去掉了 只要链表不是空的 那么curNode.next都可以条粗递归
curNode.next.next = curNode
curNode.next = null
// }
return p
}
return helper(head)
};
递归法二
var reverseList = function(head) {
//这种方法是从第一个结点开始向前翻转,一直到最后一个结点
const helper = (pre,head) =>{
//递归跳出的条件
if(head==null) return pre
let temp = head.next
head.next = pre
pre = head
//向后进行遍历
return helper(pre,temp) //整个递归的最后返回值是pre 也就是原链表的最后一个结点
}
return helper(null,head)
};
使用栈来进行翻转
var reverseList = function(head) {
//使用栈 将所有的结点全都压入栈中 然后创建一个虚拟结点 然后栈中的结点出栈
if(head==null||head.next==null) return head
let stack = []
let cur = head
//全部入栈
while(cur){
stack.push(cur)
cur = cur.next
}
let newNode = new ListNode(0,null)
let curNode = newNode
while(stack.length){
let node = stack.pop()
curNode.next = node
curNode = node
}
curNode.next = null//防止最后形成一个圈
return newNode.next
};
24.两两交换链表中的结点
使用队列进行交换
var swapPairs = function(head) {
if(head==null||head.next==null) return head
let queue = []
let cur = head
while(cur){
queue.push(cur)
cur = cur.next
}
let newNode = new ListNode(0,null)
let now = newNode
//当队列中有偶数个结点
while(queue.length>=2){
let node1 = queue.shift()
let node2 = queue.shift()
//在使用队列或者栈,我试了好多次 在出栈之前将 每个节点的next指针置为空 这样才能避免
// node1.next = null
// node2.next = null
now.next = node2
now = node2
now.next = node1
now = node1
}
//当只剩下一个节点了
while(queue.length==1){
let node1 = queue.shift()
// node1.next = null
now.next = node1
now = node1
}
now.next = null //修改一下 将最后一个结点的next 置为空即可
return newNode.next
};
原地进行交换
var swapPairs = function(head) {
//原地进行交换
if(head==null||head.next==null) return head
//进行交换
let newHead = new ListNode(0,head) //创建一个虚拟节点
let curNode = newHead.next
let pre = newHead
while(curNode&&curNode.next){
let nextNode = curNode.next.next //保存结点
pre.next = curNode.next
pre = curNode
curNode.next.next = curNode
curNode.next = nextNode
curNode = nextNode
}
//最后只剩下一个结点 就不用进行交换了
return newHead.next
};
递归实现
参考
var swapPairs = function(head) {
//和翻转链表很类似
const helper = head =>{ //判断条件head==null是用来判断链表是否为空的
if(head==null||head.next==null) return head
//每次返回的节点是处理好之后的
let nextNode = head.next
head.next = helper(nextNode.next)
nextNode.next = head
return nextNode
}
return helper(head)
};
19.删除链表的倒数第n个结点
方法一:使用两次循环找到第n个节点的位置
var removeNthFromEnd = function(head, n) {
//方法一:从头开始进行遍历找到第n个结点
if(head==null) return head
let sum = 1 //统计链表的总长度
let cur = head
while(cur.next){
sum++
cur = cur.next
}
//如果链表的长度不够
if(n>sum) return head
if(n==sum){
return head.next
}
//也即是删除第 sum-n+1个结点 需要找它前面的结点
let i = 1
cur = head
while(i<sum-n){
i++
cur = cur.next
}
cur.next = cur.next.next
return head
};
方法二:使用快慢指针
var removeNthFromEnd = function(head, n) {
//使用快慢指针 当fast指针走了n个位置之后 slow指针开始走动 当fast走到空的时候 slow所指的位置就是倒数第n个位置
if(head==null) return head
let slow = head, fast = head
let i = 1 //记录位置
//首先fast指针要移动到第n+1个位置
while(fast.next&&i<=n){
i++
fast = fast.next
}
if(i<n) return head //也就是说链表的长度小于n
if(i==n) return head.next//链表的长度正好是n
//链表的长度够了 那么slow fast同时向前移动
while(fast.next){
fast = fast.next
pre = slow
slow = slow.next //slow正好是要删除的位置的前面
}
slow.next = slow.next.next
return head
};
面试题02.07.链表相交
var getIntersectionNode = function(headA, headB) {
if(headA==null||headB==null) return null
//首先找出两个链表的长度
let cur = headA
let sumA = 1,sumB = 1
while(cur.next){
sumA++
cur = cur.next
}
cur = headB
while(cur.next){
sumB++
cur = cur.next
}
// console.log(sumA,sumB)
let len = Math.abs(sumA - sumB)
// console.log(len,'changdu ')
if(sumA>sumB){
//A链表先去移动len个位置
while(len-- >0){
headA = headA.next
}
}
if(sumA<sumB){
while(len-- >0){
headB = headB.next
}
}
// console.log(headA,headB,'jiedian')
//移动完成之后 a b 后面的长度相同
while(headA){
if(headA==headB) return headA
headA = headA.next
headB = headB.next
}
return null
};
141.环形链表
使用快慢指针
var hasCycle = function(head) {
//使用快慢指针 slow每次走一个位置 fast每次走两个位置 如果链表中有圈 那么fast一定会转一圈然后赶上slow,可以这么理解,slow相对于的fast的位置是不变的,fast相对于slow每次走一个位置,这样如果链表中有圈,那么fast一定可以回到原来的位置
if(head==null||head.next==null) return false
let slow = head,fast = head
while(fast.next){
fast = fast.next
if(fast.next){
fast = fast.next
}
slow = slow.next
// if(slow==fast){ //这样会出现错误 由于上面 进行while循环的时候,fast可以只走一个位置,而此时slow也走一个位置 这样即使是一条链 也会相等
// return true
// }
if(fast.next==slow){
return true
}
}
return false
};
快慢指针修改
var hasCycle = function(head) {
//使用快慢指针 slow每次走一个位置 fast每次走两个位置 如果链表中有圈 那么fast一定会转一圈然后赶上slow,可以这么理解,slow相对于的fast的位置是不变的,fast相对于slow每次走一个位置,这样如果链表中有圈,那么fast一定可以回到原来的位置
if(head==null||head.next==null) return false
let slow = head,fast = head
while(fast&&fast.next){
fast = fast.next.next
slow = slow.next
if(slow==fast){
return true
}
}
return false
};
142.环形链表2
方法一:使用map保存节点信息
var detectCycle = function(head) {
if(head==null||head.next==null) return null
//使用map对象来保存遍历过的结点
let map = new Map()
let cur = head
while(cur.next){
if(map.has(cur)){
//如果map中有这个结点 那么这个结点就是圈的起点
return cur
}else{
map.set(cur,1)
}
cur = cur.next
}
return null
};
方法二:快慢指针
思路:
①当slow指针和fast指针相遇的时候,slow走了x+y;fast走了x+y+n(y+z);
②由于fast的速度是slow的2倍,所以有2(x+y)=x+y=n(y+z)
③移项得x = n (y + z) - y
④令n=1得到x=z,也即是当slow和fast第一次相遇的时候,如果这时候有一个结点temp从head出发,那么temp和slow同时移动,当temp==slow的时候,temp就是入口结点
参考代码随想录
var detectCycle = function(head) {
if(head==null||head.next==null) return null
let slow = head,fast = head
while(fast&&fast.next){
fast = fast.next.next
slow = slow.next
if(slow==fast){
//既然是有环的 那么就要去找这个环的入口
fast = head
while(fast!==slow){
fast = fast.next
slow = slow.next
}
return fast
}
}
return null //没有圈
};