数据结构与算法之美二之手写LinkedList

数据结构与算法之美二之手写LinkedList

Scroll Down

专栏第二篇,本文主要内容是手写一个 LinkedList。

知识图谱

先呈上来链表章节的知识图谱。

在这里插入图片描述

Talk is cheap. Show me the code.
-- Linus Torvalds

手写 LinkedList

package com.shockang.study.algorithm.archive.linkedlist

import java.util.{NoSuchElementException, StringJoiner}

/**
 * 手写 LinkedList
 *
 * @author Shockang
 */
class MyLinkedList[T] {

  //单个结点
  private class Node(_prev: Node, _item: T, _next: Node) {
    //值
    var item: T = _item
    //前驱指针
    var prev: Node = _prev
    //后驱指针
    var next: Node = _next
  }

  //获取下标为 index 的结点
  private def node(index: Int): Node = {
    var x: Node = null
    //那边近从那边遍历
    if (index < (size >> 1)) {
      x = first
      for (_ <- 0 until index) x = x.next
    } else {
      x = last
      for (_ <- size - 1 until index by -1) x = x.prev
    }
    x
  }

  //列表中删除指定结点
  private def unlink(x: Node): T = {
    val t: T = x.item
    val next: Node = x.next
    val prev: Node = x.prev
    //有助于 GC
    x.prev = null
    x.item = null.asInstanceOf[T]
    x.next = null
    //前结点
    if (prev == null) first = next
    else prev.next = next
    //后结点
    if (next == null) last = prev
    else next.prev = prev
    size -= 1
    t
  }

  //首结点
  private var first: Node = _

  //尾结点
  private var last: Node = _

  //列表大小
  var size: Int = 0

  //列表是否为空,注意定义为 def不能为 var,不然值初始化后不会改变
  def isEmpty: Boolean = size == 0

  //以下方法为查询相关的=========================================================
  //根据下标获取对应数据
  def get(index: Int): T = {
    //检测下标是否越界
    checkRange(index)
    node(index).item
  }

  //获取第一个元素,不存在抛异常NoSuchElementException
  def getFirst: T = {
    assert(!isEmpty, throw new NoSuchElementException)
    first.item
  }

  //获取最后一个元素,不存在抛异常NoSuchElementException
  def getLast: T = {
    assert(!isEmpty, throw new NoSuchElementException)
    last.item
  }

  //获取第一个元素
  def element: T = getFirst

  //获取第一个元素,不存在返回 null
  def peek: T = if (isEmpty) null.asInstanceOf[T] else first.item

  //获取第一个元素,不存在返回 null
  def peekFirst: T = peek

  //获取最后一个元素,不存在返回 null
  def peekLast: T = if (isEmpty) null.asInstanceOf[T] else last.item

  //获取第一个匹配的元素对应下标,不存在则返回-1,这里要区分是否为 null
  def indexOf(t: T): Int = {
    var index: Int = 0
    var cur: Node = first
    if (t == null) {
      while (cur != null) {
        //index = 0 的情况下可以得出第一行必定是
        if (cur.item == null) return index
        //cur 和 index 起始对应,同时变化
        cur = cur.next
        index += 1
      }
    } else {
      while (cur != null) {
        //index = 0 的情况下可以得出第一行必定是
        //使用 equals 匹配
        if (t.equals(cur.item)) return index
        //cur 和 index 起始对应,同时变化
        cur = cur.next
        index += 1
      }
    }
    -1
  }

  //获取最后一个匹配的元素对应下标,不存在则返回-1
  def lastIndexOf(t: T): Int = {
    var index: Int = size
    var cur: Node = last
    if (t == null) {
      while (cur != null) {
        //起始index 比 cur 多一步,同时变化
        index -= 1
        // index = size - 1 可以得出必定放在第二行
        if (cur.item == null) return index
        cur = cur.prev
      }
    } else {
      while (cur != null) {
        //起始index 比 cur 多一步,同时变化
        index -= 1
        // index = size - 1 可以得出必定放在第二行
        //使用 equals 匹配
        if (t.equals(cur.item)) return index
        cur = cur.prev
      }
    }
    -1
  }

  //是否包含某个元素
  def contains(t: T): Boolean = indexOf(t) != -1

  //以下方法为新增相关的=========================================================
  //新增一个元素到链表末尾
  def add(t: T): Boolean = {
    linkLast(t)
    true
  }

  //下标为 index 的地方插入一个元素
  def add(index: Int, t: T): Unit = {
    checkRange(index)
    if (size == index) linkLast(t)
    else linkBefore(t, node(index))
  }

  //头部添加一个元素
  private def linkFirst(t: T): Unit = {
    val temp: Node = first
    //改中引用
    val newNode: Node = new Node(null, t, temp)
    first = newNode
    //改后引用
    if (temp == null) last = newNode
    else temp.prev = newNode
    size += 1
  }

  //末尾添加一个元素
  private def linkLast(t: T): Unit = {
    val temp: Node = last
    //改中引用
    val newNode: Node = new Node(temp, t, null)
    last = newNode
    //改前引用
    if (temp == null) first = newNode
    else temp.next = newNode
    size += 1
  }

  //在后面一个结点前面插入一个新结点
  private def linkBefore(t: T, after: Node): Unit = {
    //前,可能为 null
    val prev: Node = after.prev
    //改中引用
    val newNode: Node = new Node(prev, t, after)
    //改前引用,注意前可能为 null
    if (prev == null) first = newNode
    else prev.next = newNode
    //改后引用
    after.prev = newNode
    size += 1
  }

  //添加一个元素到链表头部
  def addFirst(t: T): Unit = {
    linkFirst(t)
  }

  //添加一个元素到链表尾部
  def addLast(t: T): Unit = {
    linkLast(t)
  }

  //新增一个元素到链表尾部
  def offer(t: T): Boolean = add(t)

  //新增一个元素到链表头部
  def offerFirst(t: T): Boolean = {
    linkFirst(t)
    true
  }

  //新增一个元素到链表尾部
  def offerLast(t: T): Boolean = add(t)

  //入栈,这里是添加到头部
  def push(t: T): Unit = linkFirst(t)

  //以下方法为删除相关的=========================================================
  //删除链表头部的元素
  def remove: T = removeFirst()

  //删除头结点,不考虑头结点不存在的情况
  private def unlinkFirst(): Unit = {
    val next: Node = first.next
    //有助于 GC
    first.item = null.asInstanceOf[T]
    first.next = null
    first = next
    if (next == null) last = null
    else next.prev = null
    size -= 1
  }

  //删除尾结点,不考虑尾结点不存在的情况
  private def unlinkLast(): Unit = {
    val prev: Node = last.prev
    //有助于 GC
    last.item = null.asInstanceOf[T]
    last.next = null
    last = prev
    if (prev == null) first = null
    else prev.next = null
    size -= 1
  }

  //删除下标为 index 的元素
  def remove(index: Int): T = {
    checkRange(index)
    unlink(node(index))
  }

  //删除第一个匹配的元素,要区分元素是否为null
  def remove(t: T): Boolean = removeFirstOccurrence(t)

  //删除第一个匹配的元素,要区分元素是否为null
  def removeFirstOccurrence(t: T): Boolean = {
    if (t == null) {
      var x: Node = first
      while (x != null) {
        if (x.item == null) {
          unlink(x)
          return true
        }
        x = x.next
      }
    }
    else {
      var x: Node = first
      while (x != null) {
        //equals代表匹配
        if (t.equals(x.item)) {
          unlink(x)
          return true
        }
        x = x.next
      }
    }
    false
  }

  //删除最后一个匹配的元素,要区分元素是否为null
  def removeLastOccurrence(t: T): Boolean = {
    if (t == null) {
      var x = last
      while (x != null) {
        if (x.item == null) {
          unlink(x)
          return true
        }
        x = x.prev
      }
    }
    else {
      var x = last
      while (x != null) {
        //equals代表匹配
        if (t.equals(x.item)) {
          unlink(x)
          return true
        }
        x = x.prev
      }
    }
    false
  }

  //删除第一个元素
  def removeFirst(): T = {
    assert(!isEmpty, throw new NoSuchElementException)
    val t: T = first.item
    unlinkFirst()
    t
  }

  //删除最后一个元素
  def removeLast(): T = {
    assert(!isEmpty, throw new NoSuchElementException)
    val t: T = last.item
    unlinkLast()
    t
  }

  //删除第一个元素,列表为空返回 null
  def poll: T = pollFirst

  //删除第一个元素,列表为空返回 null
  def pollFirst: T = if (isEmpty) null.asInstanceOf[T] else removeFirst()

  //删除最后一个元素,列表为空返回 null
  def pollLast: T = if (isEmpty) null.asInstanceOf[T] else removeLast()

  //删除第一个元素,列表为空抛异常 NoSuchElementException
  def pop: T = removeFirst()

  //以下为其他操作=======================================
  //下标为 index 的替换,返回旧值
  def set(index: Int, t: T): T = {
    checkRange(index)
    val temp: Node = node(index)
    val oldValue: T = temp.item
    temp.item = t
    oldValue
  }

  //清空列表
  def clear(): Unit = {
    var x: Node = first
    var next: Node = null
    while (x != null) {
      next = x.next
      x.prev = null
      x.item = null.asInstanceOf[T]
      x.next = null
      x = next
    }
    first = null
    last = null
    size = 0
  }

  override def toString: String = {
    val sj: StringJoiner = new StringJoiner(",")
    var node: Node = first
    while (node != null) {
      sj.add(node.item.toString)
      node = node.next
    }
    "[" + sj.toString + "]"
  }


  //检查下标是否越界
  private def checkRange(index: Int): Unit = {
    if (index < 0 || index >= size) {
      throw new IndexOutOfBoundsException("下标越界:" + index)
    }
  }

}

测试

package com.shockang.study.algorithm.archive.linkedlist

object Main extends App {
  val list: MyLinkedList[Int] = new MyLinkedList[Int]()
  out(list)
  //新增
  list.add(0)
  list.add(1)
  list.add(2)
  list.add(3)
  out(list)
  list.add(4)
  list.add(5)
  list.add(6)
  out(list)
  list.add(7)
  list.add(8)
  list.add(9)
  out(list)
  list.add(4, 31)
  out(list)
  list.addFirst(-1)
  out(list)
  list.addLast(10)
  out(list)
  list.offer(9)
  out(list)
  list.offerFirst(-2)
  out(list)
  list.push(-3)
  out(list)
  //修改
  list.set(4, 10)
  out(list)
  //删除
  list.remove
  out(list)
  println(list.removeFirstOccurrence(10))
  out(list)
  println(list.removeLastOccurrence(9))
  out(list)
  println(list.removeFirst())
  out(list)
  println(list.removeLast())
  out(list)
  println(list.pop)
  out(list)
  list.clear()
  out(list)

  def out(list: MyLinkedList[Int]): Unit = {
    println("****************************************")
    try {
      println(list.size)
      println(list.getFirst)
      println(list.getLast)
      println(list.get(list.size - 2))
      println(list.contains(-1))
      println(list.peek)
      println(list.toString)
    } catch {
      case x: Exception => println(x)
    }
    println("****************************************")
  }
}

输出

****************************************
0
java.util.NoSuchElementException
****************************************
****************************************
4
0
3
2
false
0
[0,1,2,3]
****************************************
****************************************
7
0
6
5
false
0
[0,1,2,3,4,5,6]
****************************************
****************************************
10
0
9
8
false
0
[0,1,2,3,4,5,6,7,8,9]
****************************************
****************************************
11
0
9
8
false
0
[0,1,2,3,31,4,5,6,7,8,9]
****************************************
****************************************
12
-1
9
8
true
-1
[-1,0,1,2,3,31,4,5,6,7,8,9]
****************************************
****************************************
13
-1
10
9
true
-1
[-1,0,1,2,3,31,4,5,6,7,8,9,10]
****************************************
****************************************
14
-1
9
10
true
-1
[-1,0,1,2,3,31,4,5,6,7,8,9,10,9]
****************************************
****************************************
15
-2
9
10
true
-2
[-2,-1,0,1,2,3,31,4,5,6,7,8,9,10,9]
****************************************
****************************************
16
-3
9
10
true
-3
[-3,-2,-1,0,1,2,3,31,4,5,6,7,8,9,10,9]
****************************************
****************************************
16
-3
9
10
true
-3
[-3,-2,-1,0,10,2,3,31,4,5,6,7,8,9,10,9]
****************************************
****************************************
15
-2
9
10
true
-2
[-2,-1,0,10,2,3,31,4,5,6,7,8,9,10,9]
****************************************
true
****************************************
14
-2
9
10
true
-2
[-2,-1,0,2,3,31,4,5,6,7,8,9,10,9]
****************************************
true
****************************************
13
-2
10
9
true
-2
[-2,-1,0,2,3,31,4,5,6,7,8,9,10]
****************************************
-2
****************************************
12
-1
10
9
true
-1
[-1,0,2,3,31,4,5,6,7,8,9,10]
****************************************
10
****************************************
11
-1
9
8
true
-1
[-1,0,2,3,31,4,5,6,7,8,9]
****************************************
-1
****************************************
10
0
9
8
false
0
[0,2,3,31,4,5,6,7,8,9]
****************************************
****************************************
0
java.util.NoSuchElementException
****************************************

个人博客:

CSDN