📌  相关文章
📜  用于双向链表合并排序的 Javascript 程序(1)

📅  最后修改于: 2023-12-03 15:27:10.513000             🧑  作者: Mango

双向链表合并排序的 JavaScript 程序

双向链表是一种常见的数据结构,它允许在数据中间进行快速的插入和删除操作。合并排序是一种高效的排序算法,它通过将一个数组分成两个部分,分别排序,然后合并这两部分,从而达到排序的目的。

在本文中,我们将介绍如何使用 JavaScript 实现双向链表的合并排序算法。我们将从定义数据结构开始,然后讨论排序算法的实现。

定义数据结构

我们首先需要定义一个双向链表的数据结构,它包含两个属性:前驱指针和后继指针。每个节点都包含一个数据元素,以及指向前驱和后继节点的指针。

class Node {
  constructor(data) {
    this.data = data;
    this.prev = null;
    this.next = null;
  }
}

class DoublyLinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.count = 0;
  }
}

在代码中,我们使用 class 关键字定义了 NodeDoublyLinkedList 两个类。Node 类表示链表中的一个节点,它有一个 data 属性用于存储数据,以及两个指针 prevnext,分别用于指向前驱和后继节点。DoublyLinkedList 类表示整个链表,它有 headtail 两个指针,分别指向链表的头节点和尾节点,以及一个 count 属性,统计链表中节点的数量。

我们还可以定义一些方法来操作双向链表,例如添加和删除节点等。这里我们只定义一个用于输出链表中所有节点的方法:

print() {
  let current = this.head;
  let result = '';
  while (current) {
    result += `${current.data} `;
    current = current.next;
  }
  console.log(result);
}
合并排序算法

有了数据结构,我们就可以开始实现双向链表的合并排序算法了。我们可以先将链表按照某个规则拆分成两个部分,然后递归地对这两部分进行排序,最后将它们合并在一起。

mergeSort(list) {
  if (!list || !list.head || !list.head.next) {
    return list;
  }
  let [left, right] = this.split(list);
  left = this.mergeSort(left);
  right = this.mergeSort(right);
  return this.merge(left, right);
}

在代码中,我们定义了一个 mergeSort 方法,它接受一个双向链表作为参数。如果链表为空或只包含一个节点,就直接返回原链表。否则,我们使用 split 方法将链表拆分成两个部分,然后递归地对这两个部分进行排序,最后使用 merge 方法将它们合并在一起。

接下来,我们实现 splitmerge 两个方法。

split

split 方法用于将双向链表按照某个规则拆分成两个部分。这里我们采用的规则是将链表分成前半部分和后半部分。我们使用 fastslow 两个指针,fast 每次走两步,slow 每次走一步,当 fast 走到链表末尾时,slow 正好走到中间节点。

split(list) {
  let fast = list.head;
  let slow = list.head;
  while (fast.next && fast.next.next) {
    fast = fast.next.next;
    slow = slow.next;
  }
  let left = list;
  let right = new DoublyLinkedList();
  right.head = slow.next;
  slow.next.prev = null;
  right.count = left.count - Math.floor(left.count / 2);
  left.tail = slow;
  left.tail.next = null;
  left.count = Math.floor(left.count / 2);
  return [left, right];
}
merge

merge 方法用于将两个有序的双向链表合并成一个有序链表。我们使用了两个指针 pq 分别指向两个链表的头节点,比较两个节点的大小,并将较小的节点插入到新链表的末尾。当其中一个链表遍历结束后,我们将另一个链表中剩余的节点插入到新链表的末尾。

merge(left, right) {
  let p = left.head;
  let q = right.head;
  let newHead = new Node(null);
  let current = newHead;
  while (p && q) {
    if (p.data <= q.data) {
      current.next = p;
      p.prev = current;
      p = p.next;
    } else {
      current.next = q;
      q.prev = current;
      q = q.next;
    }
    current = current.next;
  }
  if (p) {
    current.next = p;
    p.prev = current;
    left.tail = right.tail;
  }
  if (q) {
    current.next = q;
    q.prev = current;
    left.tail = right.tail;
  }
  let result = new DoublyLinkedList();
  result.head = newHead.next;
  result.count = left.count + right.count;
  return result;
}
完整代码
class Node {
  constructor(data) {
    this.data = data;
    this.prev = null;
    this.next = null;
  }
}

class DoublyLinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.count = 0;
  }

  print() {
    let current = this.head;
    let result = '';
    while (current) {
      result += `${current.data} `;
      current = current.next;
    }
    console.log(result);
  }

  split(list) {
    let fast = list.head;
    let slow = list.head;
    while (fast.next && fast.next.next) {
      fast = fast.next.next;
      slow = slow.next;
    }
    let left = list;
    let right = new DoublyLinkedList();
    right.head = slow.next;
    slow.next.prev = null;
    right.count = left.count - Math.floor(left.count / 2);
    left.tail = slow;
    left.tail.next = null;
    left.count = Math.floor(left.count / 2);
    return [left, right];
  }

  merge(left, right) {
    let p = left.head;
    let q = right.head;
    let newHead = new Node(null);
    let current = newHead;
    while (p && q) {
      if (p.data <= q.data) {
        current.next = p;
        p.prev = current;
        p = p.next;
      } else {
        current.next = q;
        q.prev = current;
        q = q.next;
      }
      current = current.next;
    }
    if (p) {
      current.next = p;
      p.prev = current;
      left.tail = right.tail;
    }
    if (q) {
      current.next = q;
      q.prev = current;
      left.tail = right.tail;
    }
    let result = new DoublyLinkedList();
    result.head = newHead.next;
    result.count = left.count + right.count;
    return result;
  }

  mergeSort(list) {
    if (!list || !list.head || !list.head.next) {
      return list;
    }
    let [left, right] = this.split(list);
    left = this.mergeSort(left);
    right = this.mergeSort(right);
    return this.merge(left, right);
  }
}
总结

在本文中,我们介绍了如何使用 JavaScript 实现双向链表的合并排序算法。我们首先定义了一个双向链表的数据结构,包含了节点、指针、以及一些常用的操作方法。然后,我们讨论了合并排序算法的实现,通过递归的方式将链表拆分成两个部分,分别进行排序,最后将它们合并在一起。在实现过程中,我们需要用到一些常见的链表操作技巧,例如快慢指针、指针移动等。