单向链表
单向链表类似于火车,有一个火车头,火车头会连接一个节点,节点上有乘客,并且这个节点会连接下一个节点,以此类推。
-
链表的火车结构
-
链表的数据结构
head 属性指向链表的第一个节点。
链表中的最后一个节点指向 null。当链表中一个节点也没有的时候,head 直接指向 null。
链表的数据结构
-
给火车加上数据后的结构
链表中的常见操作 -
append(element) 向链表尾部添加一个新的项。
-
insert(position, element) 向链表的特定位置插入一个新的项。
-
get(position) 获取对应位置的元素。
-
indexOf(element) 返回元素在链表中的索引。如果链表中没有该元素就返回-1。
-
update(position, element) 修改某个位置的元素。
-
removeAt(position) 从链表的特定位置移除一项。
-
remove(element) 从链表中移除一项。
-
isEmpty() 如果链表中不包含任何元素,返回 trun,如果链表长度大于 0 则返回 false。
-
size() 返回链表包含的元素个数,与数组的 length 属性类似。
-
toString() 由于链表项使用了 Node 类,就需要重写继承自 JavaScript 对象默认的 toString 方法,让其只输出元素的值。
单向链表的封装
创建单向链表类
先创建单向链表类 LinkedList,添加基本属性,再逐步实现单向链表的常用方法。
function LinkedList() {
function NewNode(data) {
this.data = data
this.next = null
}
this.head = null
this.length = 0
}
实现 append() 方法
LinkedList.prototype.append = function(data){
let node = new NewNode(data)
if(this.length === 0){
this.head = node
} else {
let current = this.head
while(current.next){
current = current.next
}
current.next = node
}
this.length += 1
}
过程图解
- 首先让 currentNode 指向第一个节点。
- 通过 while 循环使 currentNode 指向最后一个节点,最后通过 currentNode.next = newNode,让最后一个节点指向新节点 newNode。
代码测试
let linkedList = new LinkedList()
// 测试 append 方法
linkedList.append("A");
linkedList.append("B");
linkedList.append("C");
console.log(linkedList);
实现 toString() 方法
LinkedList.prototype.toString = function(){
let str = ''
let current = this.head
// 遍历所有的节点,拼接为字符串,直到节点为 null
while(current){
str += current.data + ' '
current = current.next
}
return str
}
代码测试
// 测试 toString 方法
console.log(linkedList.toString()); //--> AA BB CC
实现 insert() 方法
LinkedList.prototype.insert = function(position, data) {
// 越界判断
if(position < 0 || position > this.length) return false;
const newNode = new Node(data);
if(position === 0){
newNode.next = this.head;
this.head = newNode;
} else {
let index = 0;
let current = this.head;
let previous = null;
while(index++ < position){
previous = current;
current = current.next;
}
newNode.next = current;
previous.next = newNode;
}
this.length += 1;
}
代码测试
// 测试 insert 方法
linkedList.insert(0, "123");
linkedList.insert(2, "456");
console.log(linkedList.toString()); //--> 123 AA 456 BB CC
实现 get() 方法
获取指定位置(position)的 data。
LinkedList.prototype.get = function(position) {
if(position < 0 || position >= this.length) return null
let index = 0
let current = this.head
while(index++ < position){
current = current.next
}
return current.data
}
代码测试
// 测试 getData 方法
console.log(linkedList.get(0)); //--> 123
console.log(linkedList.get(1)); //--> AA
实现 indexOf() 方法
indexOf(data) 返回指定 data 的 index,如果没有,返回 -1。
代码实现
LinkedList.prototype.indexOf = function(data) {
let index = 0
let current = this.head
while(current){
if(current.data === data){
return index
}
current = current.next
index += 1
}
return -1
}
代码测试
// 测试 indexOf 方法
console.log(linkedList.indexOf("AA")); //--> 1
console.log(linkedList.indexOf("ABC")); //--> -1
实现 update() 方法
update(position, data) 修改指定位置节点的 data。
代码实现
LinkedList.prototype.update = function(position, newData) {
// 越界判断
if(position < 0 || position >= this.length) return false;
let current = this.head;
let index = 0;
while(index++ < position){
current = current.next;
}
current.data = newData;
return true;
}
代码测试
// 测试 update 方法
linkedList.update(0, "12345");
console.log(linkedList.toString()); //--> 12345 AA 456 BB CC
linkedList.update(1, "54321");
console.log(linkedList.toString()); //--> 12345 54321 456 BB CC
实现 removeAt() 方法
removeAt(position) 删除指定位置的节点。
代码实现
LinkedList.prototype.removeAt = function(position) {
// 越界判断
if(position < 0 || position >= this.length) return null;
let current = this.head;
if(position == 0){
this.head = this.head.next;
} else {
let index = 0;
let previous = null;
while(index++ < position){
previous = current;
current = current.next;
}
previous.next = current.next;
}
this.length -= 1;
return current.data;
}
代码测试
// 测试 removeAt 方法
linkedList.removeAt(3);
console.log(linkedList.toString()); //--> 12345 54321 456 CC
实现 remove() 方法
remove(data) 删除指定 data 所在的节点。
代码实现
LinkedList.prototype.remove = function(data) {
// 获取data在列表中的位置
const position = this.indexOf(data)
return this.removeAt(position)
}
代码测试
// 测试 remove 方法
linkedList.remove("CC");
console.log(linkedList.toString()); //--> 12345 54321 456
实现 isEmpty() 方法
isEmpty() 判断链表是否为空。
代码实现
LinkedList.prototype.isEmpty = function() {
return this.length == 0;
}
代码测试
// 测试 isEmpty 方法
console.log(linkedList.isEmpty()); //--> false
实现 size() 方法
size() 获取链表的长度。
代码实现
LinkedList.prototype.size = function() {
return this.length;
}
代码测试
// 测试 size 方法
console.log(linkedList.size()); //--> 3
完整实现
function LinkedList(){
// 内部类,节点类
function Node(data){
this.data = data;
this.next = null;
}
// 属性
this.head = null;
this.length = 0;
LinkedList.prototype.append = function(data) {
// 创建一个新节点
const newNode = new Node(data);
//判断是否添加的第一个节点
if(this.length == 0){ // 是第一个节点
this.head = newNode;
} else { // 不是第一个节点
let current = this.head;
while(current.next){ // current.next为null,表示找到了最后一个元素
current = current.next;
}
current.next = newNode;
}
this.length += 1;
}
LinkedList.prototype.toString = function() {
let current = this.head;
let linkString = ''
while(current){
linkString += current.data + ' ';
current = current.next;
}
return linkString;
}
LinkedList.prototype.insert = function(position, data) {
// 越界判断
if(position < 0 || position > this.length) return false;
const newNode = new Node(data);
if(position == 0){
newNode.next = this.head;
this.head = newNode;
} else {
let index = 0;
let current = this.head;
let previous = null;
while(index++ < position){
previous = current;
current = current.next;
}
newNode.next = current;
previous.next = newNode;
}
this.length += 1;
}
LinkedList.prototype.get = function(position) {
// 越界判断
if(position < 0 || position >= this.length) return null;
let current = this.head;
let index = 0;
while(index++ < position){
current = current.next;
}
return current.data
}
LinkedList.prototype.indexOf = function(data) {
let current = this.head;
let index = 0;
while(current){
if(current.data === data){
return index;
}
current = current.next;
index += 1;
}
return -1;
}
LinkedList.prototype.update = function(position, newData) {
// 越界判断
if(position < 0 || position >= this.length) return false;
let current = this.head;
let index = 0;
while(index++ < position){
current = current.next;
}
current.data = newData;
return true;
}
LinkedList.prototype.removeAt = function(position) {
// 越界判断
if(position < 0 || position >= this.length) return null;
let current = this.head;
if(position == 0){
this.head = this.head.next;
} else {
let index = 0;
let previous = null;
while(index++ < position){
previous = current;
current = current.next;
}
previous.next = current.next;
}
this.length -= 1;
return current.data;
}
LinkedList.prototype.remove = function(data) {
// 获取data在列表中的位置
const position = this.indexOf(data)
return this.removeAt(position)
}
LinkedList.prototype.isEmpty = function() {
return this.length == 0;
}
LinkedList.prototype.size = function() {
return this.length;
}
}