小试牛刀-js实现双链表

478 阅读4分钟

JS实现双链表

前要: 卑微的只是想把刚掌握的知识点,记录下来,好在方便查询和回顾自己所学的知识点,如果有什么错误的,希望能高抬贵手,指出其中错误。

已经有两三个月没有写掘金博文了,前面也写了一点关于数据结构的知识,也很皮毛,今天就分享下链表中的双链表,前面也分享过很粗糙的单链表的知识点,可快速查看。

**双链表:**是链表中的一种,是一种拥有前指针和后指针,分别指向前驱节点和后继节点,主要是解决了前一个节点问题,在实际用途中,当我们实际上想用到上一个节点的数据,对于单链表来说,是需要重新遍历的。

双链表的结构

双链表实现

本文将实现双链表的几个主要方法,分别为:

  • append 添加到尾部

  • indexOf 返回某个元素的位置

  • indexEle 返回某个位置的元素

  • insert插入某个位置

  • removeAt 移除某个位置

  • removeEle 移除某个元素

  • remove 移除最后一个元素

  • getSize 返回双链表的长度

  • isEmpty 返回双链表是否是空的

  • getHead 返回双链表的头部

  • getTail 返回双链表的尾部

  • string 返回字符串

  • getCurrentAndPrev 返回[当前元素,prev节点]

定义Node节点

class Node {
	constructor(element) {
		this.element = element;
		this.next = null;
		this.prev = null; // 比单链表多出了一个pre,前指针来记录上一个节点。
	}
}

定义双链表

  1. 由于我们需要记录headtail节点,故较单链表我们需要新增tail节点来记录最后一个节点。
class DoubleLinked {
	constructor() {
		this.head = null;
		this.tail = null;
		this.count = 0;
	}
}

append添加尾部元素

  1. 判断条件:若headnull,需要将当前新增的节点赋值为head
  2. head不为null,则需要遍历到最后一个元素current,将next赋值,还需要修改prev指针指向current
  3. 每次新增都需要将count+1
append(element) {
    const node = new Node(element);
    let current = this.head;
    if (!current) {
        this.head = node;
        this.tail = node;
    } else {
        while (current.next) {
            current = current.next
        }
        current.next = node;
        node.prev = current;
        this.tail = node; // 也可以直接使用this.tail.next = node;
    }
    this.count++;
}

insert:向某个位置插入元素

  1. 判断,考虑越界情况
  2. position = 0 需要判断this.head存在与否
  3. position > 0 && position < this.count 找出上一个值preNode,将尾指针指向nodenodeprev指向preNodenode的尾指针指向nextNodenextNodeprev指针指向node,count+1
  4. position = this.count, 可直接采用append方法,tail指向该添加的node
  5. position > this.count; 返回false
/**
	 * insert 在某个位置插入某个值
	 * params {
	 * 	element:插入的元素
	 * 	position: 某个位置
	 * }
	 * 判断
	 * 1。 需要判断越界情况,
	 * 	position = 0 需要判断this.head存在与否
	 * 	position > 0 && position < this.count 找出上一个值preNode,将尾指针指向node,node的prev指向preNode,node的尾指针指向nextNode,nextNode的prev指针指向node
	 * 	position = this.count, 直接采用append方法
	 * 	position > this.count; 返回false 
	 */
		
	 insert(element, position) {
		let current = this.head;
		const node = new Node(element);
		let previous = null;
		// position 为 0,需要考虑到head是否为null
		if(position === 0) {
			// 若head为null
			if(!current) {
				this.head = node // 直接将head赋值为node
				this.tail = node // 同时也需要将tail赋值为node
			}else {
				node.next = current; // node替换current,node的next指针指向current
				current.prev = node;
				this.head = node;
				// 这里不用this.tail指向,是因为我们只是替换head,tail还是一样指向的是最后一个元素;
			}
			this.count++;
		}else if (position > 0 && position <= this.count){
			// 找出position所在的元素
			let index = 0
			while(index < position) {
				previous = current;
				current = current.next;
				index++;
			}
			// 如果是最后一个元素length,则current为null,node作为最后一个元素
			if(index !== this.count) {
				node.next = current;
				current.prev = node;
			}else {
				this.tail = node;
			}
				previous.next = node
				node.prev = previous;
				this.count++;
		}else {
			return false
		}
	 }

indexOf:找出当前元素的索引值

  1. 判断考虑边界问题,超出没找到则返回-1
  2. 遍历查找,找到返回index索引,这里都是默认找出第一个元素,后面有相同元素暂时不考虑
// 找出当前的位置的元素
	indexOf(element) {
		// 根据current = current.next,求出当前位置
		let index = 0;
		let current = this.head;
		while(current && index < this.count){
			if(current.element === element) {
				return index
			}
			current = current.next;
			index++;

		}
		return -1;
	}

indexEle: 找出当前位置所在的元素

  1. 判断考虑边界问题,超出没找到则返回false,找到则返回当前元素
indexEle(position) {
    let ele = null;
    if(position >= 0 && position < this.count){
        let current = this.head;
        while(position > 0) {
            current = current.next;
            position--;
        }
        ele = current;
    }
    return ele
}

removeAt: 删除第几个元素

  1. 判断,考虑边界溢出问题。
  2. position===0 && head时,需要将head赋值为ele.next,同时将ele.prev设置为null
  3. position > 0 && head && postion < count - 1时,将previous.next = current.next;current.next.prev = previous
  4. position === count - 1,则只需要将previous.next = null;this.tail = previous;
  5. 其他情况,!head || position > count-1都返回fasle
/**
		* removeAt 删除第几个元素
		* 
		*/
removeAt(position) {
    // 找到当前位置的元素
    let ele = this.indexEle(position);
    // 这里已经是判断了越界情况了。
    if(ele) {
        // 需要考虑poistion === 0时,将prev指针指向null
        if(position === 0) {
            this.head = ele.next;
            ele.prev = null;
        }else {
            // 找出前一个指针元素
            let previous = this.indexEle(position - 1);
            // 同样情况,面对边界情况next=null需要考虑
            if(position === this.count - 1) {
                previous.next = null;
                this.tail = previous;
                // console.log({'tail': this.tail})
            }else {
                previous.next = ele.next;
                ele.next.prev = previous
            }
        }
        // 数量减一
        this.count--;
        return true
    }
    return false
}

removeEle: 删除元素

  1. 考虑元素重复问题,需要将所有元素相等的都删除
/**
		 * removeEle 删除元素
		 * 考虑元素重复问题,需要将所有element相等的都删除
		 */
		removeEle(element){
			let current = this.head;
			let previous = null;
			let num = 0
			if(current.element === element) {
				// 需要去掉是否是第一个元素,因为后面是从current.next开始的,所以当head存在ele时是删除不掉的
				this.head = current.next;
				current.next.prev = null;
				this.count--;
				num++
			}
			previous = current;
			current = current.next;
			
			while(current) {
				if(current.element === element) {
					previous.next = current.next;
					// 若不是尾指针,需要执行前指针指向
					if(current.next){
						current.next.prev = previous; 
					}else {
						this.tail = previous
					}
					this.count--; //长度-1
					num++
				}else {
					previous = current;
				}
				current = current.next;
			}
			return num === 0 ? -1 : num;
		}

remove删除最后一个元素

remove() {
    let current = this.head;
    // 删除时head为null
    if(this.count === 0) {
        return false
    }else if (this.count === 1) { // 删除时只剩下head
        this.head = null;
        this.tail = null;
        this.count--;
        return current.element
    }else {
        // 删除时,删除最后一个,需要将tail重新赋值为previous
        let previous = null
        while(current.next) {
            previous = current;
            current = current.next;
        }
        previous.next = null;
        this.tail = previous;
        this.count--;
        return current.element;
    }
}

stirng: 转成字符串

string() {
    let str = '';
    let current = this.head;
    while (current) {
        str += current.element;
        current = current.next;
    }
    return str;
}

getHead: 获取head节点

getHead() {
    return this.head && this.head.element || null;
}

getTail:获取最后一个节点

getTail() {
		return this.tail && this.tail.element || null;
	}

getSize:获取长度大小

getSize() {
		return this.count;
	}

isEmpty: 判断是否是空的

	isEmpty() {
		return this.length === 0;
	}

getCurrentAndPrev: 获取当前元素和上一个节点

getCurrentAndPrev() {
		const arr = [];
		let current = this.head;
		while (current) {
			arr.push([current.element, current.prev])
			current = current.next;
		};
		return arr
	}

测试数据

const doubleLinked = new DoubleLinked()
// console.log(doubleLinked.string());


doubleLinked.append(2);
// console.log({'head1': doubleLinked.getHead()})
// console.log({'tail1': doubleLinked.getTail()})
doubleLinked.append(3);
console.log(doubleLinked.remove())
console.log(doubleLinked.remove())
// console.log({'head2': doubleLinked.getHead()})
// console.log({'tail2': doubleLinked.getTail()})

return;
doubleLinked.append(4);
doubleLinked.append('A');
doubleLinked.append('C');
doubleLinked.append('A');
// console.log({'head3': doubleLinked.getHead()})
// console.log({'tail3': doubleLinked.getTail()})
doubleLinked.insert('A', 0)
// console.log(doubleLinked.getSize())

doubleLinked.insert('B', 4)

doubleLinked.insert('C', 3)

console.log(doubleLinked.string());

// doubleLinked.removeAt(5)
// console.log(doubleLinked.indexEle(3))
// console.log(doubleLinked.indexOf('C'))
// console.log(doubleLinked.insert('D', 10)) // false
// console.log({'tail3': doubleLinked.getTail()})
console.log(doubleLinked.removeEle('A'))
console.log({'tail3': doubleLinked.getTail()})
console.log(doubleLinked.getSize())
console.log(doubleLinked.string());
console.log(doubleLinked.getCurrentAndPrev())