漫画算法-学习笔记(03)

漫画算法-小灰的算法之旅(03)

1. 什么是链表


链表(linked list):是一种在物理上非连续非顺序的数据结构,由若干节点(node)所组成。

单向链表:每一个节点又包含两部分,一部分是存放数据的变量data;另一部分是指向下一个节点的指针next.

private static class Node{
	int data;
	Node next;
}

链表的第一个节点被称为头节点,最后一个节点被称为尾节点,尾节点的next指针指向null。对于链表的其中一个节点A,我们只能根据节点A的next指针来找到该节点的下一个节点B,再根据节点B的next指针找到下一个节点C,依次单线传递。

什么是双向链表?


双向链表:比单向链表稍微复杂一点,它的每一个节点除了拥有data和next指针外,还拥有指向前置节点的prev指针。


链表的存储方式


如果说数组在内存中的存储方式是顺序存储,那么链表在内存中的存储方式则是随机存储。 链表的每一个节点在内存的不同位置,依靠next指针关联起来。这样可以灵活有效的利用零散的碎片空间。

2. 链表的基本操作


查找节点

在查找元素时,链表不像数组那样可以通过下标快速进行定位,只能从头节点开始向后一个一个节点逐一查找。

链表中的数据只能按顺序进行访问,最坏的时间复杂度为O(n)。


更新节点

如果不考虑查找节点的过程,链表的更新过程会像数组那样简单,直接把旧数据换成新数据即可。


插入节点

与数组类似,链表插入节点时,同样分三种情况:

  • 尾部插入
  • 头部插入
  • 中间插入

尾部插入:是最简单的情况,把最后的一个节点的next指针指向新插入的节点即可。

头部插入:第一步:把新节点的next指针指向原先的头节点;第二步:把新节点变为链表的头节点。

中间插入:可以分为两个步骤完成:第一步:新节点的next指针,指向插入位置的节点。第二步:插入位置前置节点的next指针,指向新节点。

只要内存空间允许,能够插入链表的元素时无穷无尽的,不需要像数组那样考虑扩容的问题。


删除节点

链表的删除操作同样分为三种情况。

  • 尾部删除
  • 头部删除
  • 中间删除

尾部删除:是最简单的情况,把倒数第二个节点的next指针指向null即可。

头部删除:也比较简单,把链表的头节点设为原先头节点 的next指针即可。

中间删除:同样很简单,把要删除节点的前置节点的next指针,指向要删除元素的下一个节点即可。

如果不考虑插入、删除操作之前查找元素的过程,只考虑纯粹的插入和删除操作,链表的插入、删除操作时间复杂度都为O(1).


3. 代码实现

实现链表的完整代码


// 头节点指针
private Node head;
// 尾节点指针
private Node last;
// 链表实际长度
private int size;

/**
* 链表插入元素
* param data 插入元素
* param index 插入位置
*/
public void insert(int data,int index) throws Exception{
  if(index<0 || index>size){
    throw new IndexOutOfBoundsException("超出链表节点范围")}
  Node insertNode =new Node(data);
  if(size == 0){
    // 空链表
    head =insertNode;
    last =insertNode;
  }else if(index ==0){
    //插入头部
    insertNode.next=head;
    head =insertNode;
  }else if (size ==index){
    //尾部插入
    last.next=insertNode;
    last =insertNode;
  }else{
    //插入中间
    Node prevNode=get(index-1);
    insertNode.next=prevNode.next;
    prevNode.next=insertNode;
  }
  size++;
}
/**
* 链表删除元素
* param index 删除的位置
*
*/
public Node remove(int index ) throws Exception{
  if(index<0 || index >size){
    throw new IndexOutOfBoundsException("超出链表节点范围");
  }
  Node removedNode =null;
  if(index ==0){
    //删除头节点
    removeNode =head;
    head=head.next;
  }else if (index ==size-1){
    //删除尾节点
    Node prevNode =get(index-1);
    removedNode =prevNode.next.next;
    prevNode.next =null;
    last =prevNode;
  }else{
    // 删除中间节点
    Node prevNode= get(index-1);
    Node nextNode= prevNode.next.next;
    removedNode=prevNode.next;
    prevNode.next=nextNode;
  }
  size--;
  return removedNode;
}

/**
* 链表查找元素
* param index 查找的位置
*/
public Node get(int index) throws Exception{
  if(index<0 || index>=size){
    throw new IndexOutOfBoundsException("超出链表节点范围")}
  Node temp =head;
  forint i=0;i<index;i++){
    temp=temp.next;
  }
  return temp;
}

/**
* 输出链表
*/

public void output(){
  Node temp=head;
  while(temp!=null){
    System.out.println(temp.data);
    temp=temp.next;
  }
}

/** 
* 链表节点
*/
private static class Node{
  int data;
  Node next;
  Node(int data){
    this.data=data;
  }
}

public static void main(Strings[] args) throws Exception{
  
  MyLinkedList myLinkedList=new MyLinkedList();
  
  myLinkedList.insert(3,0);
  myLinkedList.insert(7,1);
  myLinkedList.insert(9,2);
  myLinkedList.insert(5,3);
  myLinkedList.insert(8,1);
  myLinkedList.insert(6,7);
  myLinkedList.remove(0);
  myLinkedList.output();
}

以上是对单链表相关操作的代码实现。为了尾部插入的方便,代码中额外增加了指向链表尾部节点的指针last。

4. 数组与链表的比较


数据结构没有绝对的好与坏,数组和链表各有千秋。数组和链表相关操作的性能比较如下表所示:

查找更新插入删除
数组O(1)O(1)O(n)O(n)
链表O(n)O(1)O(1)O(1)

从表格可以看出,数组的优势在于能够快速定位元素,对于读操作多,写操作少的场景来说,用数组更合适一些。

链表的优势在于能够灵活的进行插入和删除操作,如果需要在尾部频繁插入、删除元素,用链表更合适一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值