2.链表

96 阅读3分钟

1.给出一个链表的head,判断该链表是否为回文结构,要求S(n) = 1,T(n) = n

//方法一:T(n) = n,S(n) = 1
function f(head){
  if(!head||!head.next) return true
  let res = true
  let n1 = head
  let n2 = head
  let n3 = null
  
  // 1.利用快慢指针找出中间点n1
  while (n2.next&&n2.next.next) {
    n1 = n1.next
    n2 = n2.next.next
  }

  // 2.反转右侧
  n2 = n1.next
  n1.next = null
  while (n2) {
    n3 = n2.next
    n2.next = n1
    n1 = n2
    n2 = n3
  }

  // 3.判断是否回文
  n3 = n1 // 这里是为了遍历完之后恢复原链表结构
  n2 = head
  while (n1&&n2) {
    if(n1.value!==n2.value) {
      res = false
      break
    }
    n1 = n1.next
    n2 = n2.next
  }

  // 4.恢复原结构,再反转右侧
  n1 = n3
  n2 = n1.next
  n1.next = null
  while(n2){
    n3 = n2.next
    n2.next = n1
    n1 = n2
    n2 = n3
  }
  return res
}
// 方法二:T(n) = n,S(n) = n,最笨的方法
function f(head){
  let stack = []
  
  let cur = head
  while(cur){
    stack.push(cur)
    cur = cur.next
  }
  cur = head
  while(cur){
    if(cur.value!==stack.pop().value) return false
    cur = cur.next
  }
  return true
}
// 方法三:T(n) = n,S(n) = n/2
function f(head){
  if(!head||!head.next) return true
  let stack = []
  let n1 = head
  let n2 = head
  while(n2.next&&n2.next.next){
    n1 = n1.next
    n2 = n2.next.next
  }
  n1 = n1.next // 中点的下一点
  while (n1) {
    stack.push(n1)
    n1 = n1.next
  }
  while(stack.length){
    if(head.value!==stack.pop().value) return false
    head = head.next
  }
  return true
}

2.给出一个链表的head,要求链表按照一个给定值,按小于、等于、大于排列,要求S(n) = 1,T(n) = n

// 方法一:T(n) = n,S(n) = n,不稳定
function f(head,n) {
  let arr = []
  let next = null
  while (head) {
    next = head.next
    head.next = null
    arr.push(head)
    head = next
  }
  partition(arr,n) // 荷兰国旗 partition
  let h = a = arr.shift()
  while (arr.length) {
    a.next = arr.shift()
    a = a.next
  }
  return h
}
function partition(arr,n,l=0,r=arr.length-1) {
  let min = l-1
  let max = r+1
  while (l<max) {
    if(arr[l].value<n) swap(arr,l++,++min)
    else if(arr[l].value>n) swap(arr,l,--max)
    else l++
  }
}
function swap(arr,i,j) {
  [arr[i],arr[j]] = [arr[j],arr[i]]
}
// 方法二:T(n) = n,S(n) = 1,稳定
function f(head,n){
  let sh=st=eh=et=bh=bt=next=null
  while (head) {
    next = head.next
    head.next = null
    if(head.value<n){
      if(!sh){
        sh = st = head
      }else{
        st.next = head
        st = head
      }
    }
    else if(head.value==n){
      if(!eh){
        eh = et = head
      }else{
        et.next = head
        et = head
      }
    }
    else if(head.value>n){
      if(!bh){
        bh = bt = head
      }else{
        bt.next = head
        bt = head
      }
    }
    head = next
  }
  if(st){
    st.next = eh
    et = et||et
  }
  if(et){
    et.next = bh
  }
  return sh||eh||bh
}

3.复制一个带有random指针的链表

// 方法一:T(n) = n,S(n) = n
function f(head) {
  let cur = head
  let map = new Map() // use map
  while (cur) { // copy value
    map.set(cur, {value:cur.value})
    cur = cur.next
  }
  cur = head
  while (cur) { // copy next&random
    map.get(cur).next = map.get(cur.next)
    map.get(cur).random = map.get(cur.random)
  }
  return map.get(head)
}
// 方法二:T(n) = n,S(n) = 1
function f(head) {

  // 1-1-2-2-3-3
  let cur = head
  while (cur) {
    let {value,next} = cur
    cur.next = {value}
    cur.next.next = next
    cur = next
  }

  // random
  cur = head
  while (cur) {
    let {next} = cur
    next.random = cur.random?cur.random.next:null // 边界
    cur = next.next
  }
  
  // 1-2-3   1-2-3
  cur = head
  let h = a = cur.next
  while (cur) {
    cur.next = cur.next.next
    a.next = a.next?a.next.next:null // 边界
    cur = cur.next
    a = a.next
  }
  return h
}

4.找出两链表(可能有环loop)的相交节点,要求:T(n)=n,S(n)=1

// 主函数
function findMergeNode(head1,head2) {
  let loop1 = getLoop(head1)
  let loop2 = getLoop(head2)
  if(!loop1&&!loop2) return noLoop(head1,head2)
  if(loop1&&loop2) return bothLoop(head1,head2,loop1,loop2)
  return null // 一个有环一个无环必不可能相交
}
// 找loop
function getLoop(head) {
  if(!head||!head.next||!head.next.next) return null
  let n1 = head.next
  let n2 = head.next.next
  while (n2.next&&n2.next.next) {
    n1 = n1.next
    n2 = n2.next.next
    if(n1===n2) {
      n2 = head
      while (n2!==n1) {
        n1 = n1.next
        n2 = n2.next
      }
      return n1
    }
  }
  return null
}
// 都无环的情况
function noLoop(head1,head2) {
  if(!head1||!head1.next||!head2||!head2.next) return null

  let step = 0 // step == n1.len - n2.len
  let n1 = head1
  let n2 = head2
  while (n1.next) {
    step++
    n1 = n1.next
  }  
  while (n2.next) {
    step--
    n2 = n2.next
  }
  if(n1!==n2) return null
  
  n1 = step>0?head1:head2
  n2 = step>0?head2:head1
  step = Math.abs(step)
  while (step) {
    step--
    n1 = n1.next
  }
  while (n1!==n2) {
    n1 = n1.next
    n2 = n2.next
  }
  return n1
}
// 都有环的情况
function bothLoop(head1,head2,loop1,loop2) {
  let n1 = null
  let n2 = null
  if(loop1==loop2){ // 情况1:两环相等必相交
    let step = 0
    n1 = head1
    n2 = head2
    while (n1!==loop1) {
      step++
      n1 = n1.next
    }
    while (n2!==loop2) {
      step--
      n2 = n2.next
    }
    n1 = step>0?head1:head2
    n2 = step>0?head2:head1
    step = Math.abs(step)
    while (step) {
      step--
      n1 = n1.next
    }
    while(n1!==n2){
      n1 = n1.next
      n2 =  n2.next
    }
    return n1
  }
  // 情况2:两环不等,从任意loop开始,若绕一圈还是没碰头,证明没有相交点
  n1 = loop1.next
  while (n1!==loop1) {
    if(n1===loop2) return loop2
    n1 = n1.next
  }

  return null
}