js中的链表操作
首先我们应该知道什么是链表
-
在我们基本使用中,储存多个元素,数组可能是最常用的数据结构。这种数据结构非常方便,但是有一个缺点:从数组的起点或者中间插入或移除的成本非常高,因为需要移动元素
-
链表存储有序的元素的集合,但是不同于数组,链表中的元素在内存中并不是连续位置的。每个元素都是由一个存储元素本身的结点和指向下一元素的引用(也叫指针或者链接)组成。
-
相比于数组来说,链表的好处在于添加或者删除元素的时候不需要移动其他元素。但是操作链表需要指针。数组的一个优点是可以直接访问任何位置的任何元素,但是要是想访问链表中的某一元素,则是必须从起点开始迭代知道找到目标元素
首先先来看一看单向链表
//这是创建链表的方法
function LinkedList(){
// 初始的头部节点
this.head=null;
// 初始长度
this.length=0;
//创建节点的方法,或者是链表的一个项
var Node=function(element){
this.element=element;//表示添加到链表的值或者删除链表的值
this.next=null;//表示指向下一个节点项的指针
}
........
//这里可以定义链表的操作方法等
}
在这里要强调一下,每一个Node被创建时,它的next指针总是NULL
在这里我们可以使用es6中的class语法糖来实现
就一个小例子
class SignleLinkLit{
constructor(){
// 初始的头部节点
this.head = null
// 初始长度
this.length = 0;
}
class Node{
constructor(element){
// 信息域
this.element = element;
// 指针域
this.next = null;
}
}
}
接下来我们可以看看它的一些操作方法
1.向单向链表的尾部追加元素
- 首先判断链表是否为空,如是,添加的是第一个元素。
- 列表不为空,向其后追加元素。
- 要循环访问列表中的所有元素,就需要有一个起点,就是head
this.append=function(element){
var node=new Node(element),//传入值创建Node项
current;
if(this.head===null){//如果为空链表
this.head=node;//设置node为head(head为第一个节点的引用)
}else{
current=this.head;//从表头开始
while(current.next){
//循环列表,找到最后一项(列表最后一个节点的下一个元素始终是null)
current=current.next;
}
//使当前最后一项的指针指向node
current.next=node;
}
this.length++;//更新链表的长度
}
2.从链表中删除某一个元素
输入位置,从特定位置移除一个元素
this.deleteOne=function(positon){
if(positon>-1&&position<length){//有效性检测
var current=this.head,//用current来循环列表
previous,
index=0;
if(position===0){
this.head=current.next;
//移除第一个元素,直接把head指向下一个元素
}else{
while(index++ < position){
//循环列表找到满足条件的那个元素
previous=current;
current=current.next;
//把下一个变量赋值给current
}
//跳过current,将当前要移除的元素的上一个与下一项直接连接起来
previous.next=current.next;
}
length--;
return current.element;
}else{
return null;
}
}
3.从链表添加元素
在任意位置插入一个元素
this.insert=function(position,element){
if(position>=0&&positon<=length){
var node=new Node(element),
current=this.head;//通过current从head位置开始迭代
previous,
index=0;
if(position===0){//第一位置
node.next=current;//此时current=this.head,指向head那么node就成了第一个
this.head=node;//node指向head
}else{
while(index++ <position){//循环迭代到目标位置
previous=current;
current=current.next;
}
node.next=current;//node的下一个为current
previous.next=node;//node的上一个位置为previous
}
length++;
return true;
}else{
return false;
}
}
4.返回元素的位置
this.indexOf=function(element){
var current=this.head,
index=0;
while(current){
if(element===current.element){
return index;//找到返回当前位置
}
index ++;
current=current.next;
}
return -1;//找不到返回-1
}
5.将链表转换为字符串
this.toDstring=function(){
var current=this.head,
string='';
while(current){//循环访问列表
string+=currant.element+(current.next?'\n':'');
current=current.next;
}
return string;//返回字符串
}
那么它的完整代码就是
//这是创建链表的方法
function LinkedList() {
// 初始的头部节点
this.head = null;
// 初始长度
this.length = 0;
//创建节点的方法,或者是链表的一个项
var Node = function (element) {
this.element = element;//表示添加到链表的值或者删除链表的值
this.next = null;//表示指向下一个节点项的指针
}
//追加节点
this.append = function (element) {
var node = new Node(element),//传入值创建Node项
current;
if (this.head === null) {//如果为空链表
this.head = node;//设置node为head(head为第一个节点的引用)
} else {
current = this.head;//从表头开始
while (current.next) {
//循环列表,找到最后一项(列表最后一个节点的下一个元素始终是null)
current = current.next;
}
//使当前最后一项的指针指向node
current.next = node;
}
this.length++;//更新链表的长度
}
//移除节点
this.deleteOne = function (positon) {
if (positon > -1 && position < length) {//有效性检测
var current = this.head,//用current来循环列表
previous,
index = 0;
if (position === 0) {
this.head = current.next;
//移除第一个元素,直接把head指向下一个元素
} else {
while (index++ < position) {
//循环列表找到满足条件的那个元素
previous = current;
current = current.next;
//把下一个变量赋值给current
}
//跳过current,将当前要移除的元素的上一个与下一项直接连接起来
previous.next = current.next;
}
length--;
return current.element;
} else {
return null;
}
}
//插入节点
this.insert = function (position, element) {
if (position >= 0 && positon <= length) {
var node = new Node(element),
current = this.head;//通过current从head位置开始迭代
previous,
index = 0;
if (position === 0) {//第一位置
node.next = current;//此时current=this.head,指向head那么node就成了第一个
this.head = node;//node指向head
} else {
while (index++ < position) {//循环迭代到目标位置
previous = current;
current = current.next;
}
node.next = current;//node的下一个为current
previous.next = node;//node的上一个位置为previous
}
length++;
return true;
} else {
return false;
}
}
//返回元素的位置
this.indexOf = function (element) {
var current = this.head,
index = 0;
while (current) {
if (element === current.element) {
return index;//找到返回当前位置
}
index++;
current = current.next;
}
return -1;//找不到返回-1
}
//转换为字符串
this.toDstring = function () {
var current = this.head,
string = '';
while (current) {//循环访问列表
string += currant.element + (current.next ? '\n' : '');
current = current.next;
}
return string;//返回字符串
}
}
其中涉及到循环链表
单向循环链表和链表唯一区别在于:最后一个元素指向下一个元素的指针不是引用null而是指向第一个元素
下面试试双向链表
下面我是用的是es6的class语法糖来写
class DoublyLinkedList{
class Node {
constructor(element) {
this.element = element;
// 前驱指针
this.prev = null;
// 后继指针
this.next = null;
}
}
constructor(){
// 初始头部节点
this.head = null;
// 初始尾部节点
this.tail = null;
// 链表的长度
this.length = 0;
}
// 操作
//获取长度
size(){
return this.length;
}
// 获取链表
getList(isInverted = false){
return isInverted ? this.tail : this.head;
}
// 清空链表
clear(){
this.head = this.tail = null;
this.length = 0;
}
// 链表是否为空
isEmpty(){
return this.length === 0;
}
// 插入节点
insert(position, element);
// 删除链表节点
deleteNode(position);
// 寻找链表节点
search(element);
}
1.插入节点的方法
在这之前,我们先来看看一张图,看他如何实现插入的
insert(position, element){
if(position < 0 || position > this.length) return null;
const node = new Node(element);
if(!this.head){
this.head = this.tail = node;
}else if(position === 0){ // 插入节点是0的话,就需要调整head指向
node.next = this.head;
this.head.prev = node;
// head指向新的头节点
this.head = node;
}else if(position === this.length){ // 是尾部
this.tail.next = node;
node.prev = this.tail;
// tail重定向
this.tail = node;
}else {//任意位置
let temp = this.head,
index = 0;
while(index < position){
temp = temp.next;
index++;
}
temp.prev.next = node;
node.prev = temp.prev;
temp.prev = node;
node.next = temp;
}
this.length++;
}
2.删除节点的方法
照旧 我们也来看一张图,理解理解
deleteNode(position){
if(!this.length || position < 0 || position > this.length - 1) return null;
let temp = this.head, index = 0;
if(this.length === 1){ // 如果仅有一个节点
this.clear();
}else if(position === 0){
this.head.next.prev = null;
this.head = this.head.next;
}else if(position === this.length - 1){
this.tail.prev.next = null;
this.tail = this.tail.prev;
}else{
while(index < position){
temp = temp.next;
index ++;
}
temp.prev.next = temp.next;
temp.next.prev = temp.prev;
}
this.length--;
return temp.element;
}
3.搜索节点
search(element){
let temp = this.head;
while(temp){
if(temp.element === element) return true;
temp = temp.next;
}
return false;
}
如何实现两个链表的合并
将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4