前端数据结构(3)之链表及其应用

245 阅读2分钟

基础

  链表是一种在物理存储单元中非连续,非顺序的数据结构,它的逻辑顺序是通过指针链接次序实现的。一个单链表的节点在Javascript中可以表示为:

function Node(data){
    this.data = data;
    this.next = null;
}

  单链表的结构示意图如下:

链表的实现

  链表需要实现的几个基本的方法是:append(向链表尾部追加节点),print(打印整个链表的元素),insert(向某个index位置插入新节点),remove(删除某个位置index的节点),get(获取某个位置index的节点),indexOf(给定某数据,查找它在链表的哪个位置)。

function LinkList() {
    let Node = function(data) {
        this.data = data;
        this.next = null;
    }

    let length = 0;
    let head = null;
    let tail = null;

    this.append = function(data) {
        let node = new Node(data)
        if (head == null) {
            head = node;
            tail = node;
        } else {
            tail.next = node;
            tail = node;
        }
        length = length + 1;
        return true;
    };

    this.print = function() {
        let curr_node = head;
        while (curr_node) {
            console.log(curr_node.data);
            curr_node = curr_node.next;
        }
    };

    this.insert = function(index, data) {
        if (index < 0 || index > length) {
            return false;
        } else if (index == length) {
            return this.append(data);
        } else {
            let node = new Node(data);
            if (index == 0) {
                node.next = head;
                head = node;
            } else {
                let insert_index = 1;
                let curr_node = head;
                while (insert_index < index) {
                    insert_index += 1;
                    curr_node = curr_node.next;
                }
                let next_node = curr_node.next;
                node.next = next_node;
                curr_node.next = node;
            }
            length = length + 1;
            return true;
        }
    };

    this.remove = function(index) {
        if (index < 0 || index >= length) {
            return false;
        } else {
            let del_node = null;
            if (index == 0) {
                del_node = head;
                head = head.next;
            } else {
                let del_index = 1;
                let curr_node = head;
                while (del_index < index) {
                    del_index += 1;
                    curr_node = curr_node.next;
                }
                del_node = curr_node.next;
                let next_node = curr_node.next.next;
                curr_node.next = next_node;

                if (del_node.next == null) {
                    tail = curr_node;
                }
            }
            length = length - 1;
            return del_node.data;
        }
    };
    
    this.get = function(index) {
        if (index < 0 || index >= length) {
            return null;
        } else {
            if (index == 0) {
                return head;
            } else {
                let curr_node = head;
                let curr = 0;
                while (curr < index) {
                    curr += 1;
                    curr_node = curr_node.next;
                }
                return curr_node.data;
            }
        }
    }
    
    this.indexOf = function(data) {
        let index = -1;
        let curr_node = head;
        while (curr_node) {
            index += 1
            if (curr_node.data == data) {
                return index;
            } else {
                curr_node = curr_node.next;
            }
        }
        return -1;
    }
}

let link = new LinkList();
link.append(2);
link.append(4);
link.append(8);
link.append(7);
link.print()

链表的应用题

  1. 反转单链表
      比较常考的一道题,有两种方式来解决,一种是迭代法,一种是递归法。迭代法就是需要定义三个节点指针,一个指向当前节点,一个指向前面一个节点,一个指向后面一个节点,反转就是说,当前节点的next指针指向前面一个节点。
      递归方法就是你不会反转当前链表,让递归方法先帮你反转当前节点的下一个节点开始的单链表,把反转后的头节点返回。你再将当前头节点连接到返回头节点的尾部。
class Node {
    constructor(data) {
        this.data = data;
        this.next = null;
    }
}
let node1 = new Node(1);
let node2 = new Node(2);
let node3 = new Node(3);
let node4 = new Node(4);
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = null;

function print(node) {
    let str = "";
    while (node) {
        str += node.data.toString() + "->";
        node = node.next;
    }
    str += "null"
    console.log(str);
}

print(node1);

function reverse(node) {
    if (!node) {
        return null;
    }
    let curr_node = node;
    let pre_node = null;
    while (curr_node) {
        let next_node = curr_node.next;
        curr_node.next = pre_node;
        pre_node = curr_node;
        curr_node = next_node;
    }
    return pre_node;
}
let node = reverse(node1);
print(node);

function reverse_digui(node) {
    if (!node) {
        return null;
    }
    if(node.next == null){
        return node;
    }
    let new_head = reverse_digui(node.next);
    node.next.next = node;
    node.next = null;
    return new_head;
}
  1. 从尾到头打印单链表
      还是用递归方法,不知道如何反向打印,就先让下一个节点所在的单链表反向打印,等全部打印完,再把自己打印出来。
function reverse_print(node){
    if(!node){
        return null;
    }
    
    if(node == null){
        return;
    }else{
        reverse_print(node.next);
        console.log(node.data);
    }
}