数据结构:用JS实现链表

626 阅读4分钟

链表特点:链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(指针或链接)组成。

相对于传统的数组,链表的一个好处在于,添加或移除元素的时候不需要移动其他元素。然而,链表需要使用指针,因此实现链表时需要额外注意。数组的另一个细节是可以直接访问任何位置的元素,而想要访问链表中间的一个元素,需要从起点开始迭代链表直到找到所需元素。

创建链表

我们先搭建一个类的骨架

function LinkedList () {
	//辅助类 表示要加入链表的项
	var Node = function (element) {
		this.element = element;
		this.next = null; //指向链表中下一个节点项的指针
	};
	
	var length = 0;
	var head = null;
	
	this.append = function (element) {}; //向链表尾部添加一个新的项
	this.insert = function (position, element) {}; //向链表特定位置插入一个新的项
	this.removeAt = function (position) {}; //从链表特定位置移除一项
	this.remove = function (element) {}; //从链表中移除一项
	this.indexOf = function (element) {}; //返回元素在链表中的索引,如果没有则返回-1
	this.isEmpty = function () {}; //判断链表是否为空
	this.size = function () {}; //返回链表包含元素个数
	this.getHead = function () {}; //返回链表第一个元素
	this.toString = function () {}; //只输出元素的值
	this.print = function () {}; //打印元素的值
}

下面,我们来一一实现他们

this.append = function (element) {
    var node = new Node(element),
        current;
    
    if (head === null) { //链表为空,添加到首部
        head = node;
    }else {
        current = head;
        //循环链表,直到找到最后一项
        while (current.next) {
            current = current.next;
        }
        //找到最后一项,将其next赋为node,建立连接
        current.next = node;
    }
    length++;
};

这里写图片描述
在普通链表中:链表最后一个节点的下一个元素始终是null。

this.removeAt = function (position) {
	//检查是否越界
    if (position > -1 && position < length) {
        var current = head,
            previous,
            index = 0;
            
        if (position === 0) { //移除第一项
            head = current.next;
        }else {
            while (index++ < position) {
                previous = current;
                current = current.next;
            }
            //将previous与current的下一项链接起来,跳过current,从而移除它
            previous.next = current.next;
        }
        length--;
        return current.element;
    }else {
        return null;
    }
};

这里写图片描述

this.insert = function (position, element) {
	//检查是否越界
    if (position >= 0 && position <= length) {
        var node = new Node(element),
            current = head,
            previous,
            index = 0;
            
        if (position === 0) { //在第一个位置添加
            node.next = current;
            head = node;
        }else {
            while (index++ < position) {
                previous = current;
                current = current.next;
            }
            //通过改变指针,将node链接在previous和current之间
            node.next = current;
            previous.next = node;
        }
        length++;
        return true;
    }else {
        return false;
    }
};

这里写图片描述

//只输出链表中元素
this.toString = function () {
    var current = head,
        string = "";
    while (current) {
        string += "," + current.element;
        current = current.next;
    }
    return string.slice(1);
}; 
this.indexOf = function (element) {
    var current = head,
        index = 0;
    while (current) {
        if (current.element === element) {
            return index;
        }
        index++;
        current = current.next;
    }
    return -1;
};
this.remove = function (element) {
    var index = this.indexOf(element);
    return this.removeAt(index);
};
this.isEmpty = function () {
    return length === 0;
}; 
this.size = function () {
    return length;
};
//head是一个私有变量,当需要在类的实现外部循环访问链表时,就可以使用getHead方法获取类的第一个元素
this.getHead = function () {
    return head;
};
this.print = function () {
	console.log( this.toString() );
};

接下来,我们在上面的基础上做点扩展

双向链表

一个链向下一个元素,另一个链向前一个元素。

这里写图片描述

function DoublyLinkedList () {
	var Node = function (element) {
		this.element = element;
		this.next = null;
		this.prev = null; //新
	};
	var length = 0;
	var head = null;
	var tail = null; //新 对最后一项的引用

	//方法
}
this.append = function (element) {
    var node = new Node(element),
        current;
    
    if (head === null) { //链表为空,添加到首部
        head = node;
        tail = node; //新
    }else {
        current = head;
        //循环链表,直到找到最后一项
        while (current.next) {
            current = current.next;
        }
        //找到最后一项,将其next赋为node,建立连接
        current.next = node;
        tail = node; //新
    }
    length++;
};
this.insert = function (position, element) {
	//检查是否越界
    if (position >= 0 && position <= length) {
        var node = new Node(element),
            current = head,
            previous,
            index = 0;
            
        if (position === 0) { //在第一个位置添加
	        if (!head) { //新
				head = node;
				tail = node;
			}else {
	            node.next = current;
	            current.prev = node; //新
	            head = node;			
			}
        }else if (position === length) { //新 最后一项
	        //改变指针,再把node赋值给tail
	        current = tail;
	        current.next = node;
	        node.prev = current;
	        tail = node;
        }else {
            while (index++ < position) {
                previous = current;
                current = current.next;
            }
            //通过改变指针,将node链接在previous和current之间
            node.next = current;
            previous.next = node;

			current.prev = node; //新
			node.prev = previous; //新
        }
        length++;
        return true;
    }else {
        return false;
    }
};
this.removeAt = function (position) {
	//检查是否越界
    if (position > -1 && position < length) {
        var current = head,
            previous,
            index = 0;
            
        if (position === 0) { //移除第一项
            head = current.next;
			//新 如果只有一项,更新tail
			if (length === 1) {
				tail = null;
			}else {
				head.prev = null;
			}
        }else if(position === length - 1) { //新 最后一项
	        current = tail;
	        tail = current.prev;
	        tail.next = null;
        }else {
            while (index++ < position) {
                previous = current;
                current = current.next;
            }
            //将previous与current的下一项链接起来,跳过current,从而移除它
            previous.next = current.next;
            current.next.prev = previous; //新
        }
        length--;
        return current.element;
    }else {
        return null;
    }
};

其它的方法和单向链表一样

扩展就到这里,有兴趣的朋友可以试试在此基础上再进行扩展 比如,循环链表:将链表最后一项的next指向head 在升级就是双向循环链表,感觉要被玩坏了~