数据结构——链表
1.什么是链表?
- 链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针连接次序实现的。
- 每一个链表都包含多个节点,节点又包含两个部分,一个是数据域(储存节点含有的信息),一个是引用域(储存下一个节点或者上一个节点的地址)。
- 链表的理解示意图:
2.链表的特点
- 获取数据麻烦,需要遍历查找,比数组慢
- 方便插入、删除
因此,链表适用于频繁插入和删除数据的场景。
3.链表的实现原理
- 创建一个节点类,其中节点类包含两个部分,第一个是数据域(你到时候要往节点里面储存的信息),第二个是引用域(相当于指针,单向链表有一个指针,指向下一个节点;双向链表有两个指针,分别指向下一个和上一个节点)
- 创建一个链表类,其中链表类包含三个属性:头结点、尾节点和大小,方法包含添加、删除、插入等等方法。
基于面向对象的思想,分别利用Java和C#两种语言实现了单向链表和双向链表。
单向链表
基于Java实现的单向链表
节点类:
class Node{
private Object element;
private Object next;
public Node() {
}
public Node(Object element) {
this.element = element;
}
public Object getElement() {
return element;
}
public void setElement(Object element) {
this.element = element;
}
public Object getNext() {
return next;
}
public void setNext(Object next) {
this.next = next;
}
@Override
public String toString() {
return "Node{" +
"element=" + element +
", next=" + next +
'}';
}
}
单向链表类:
public class MyLinkedList {
private int length;
private Object head;
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public Object getHead() {
return head;
}
public void setHead(Object head) {
this.head = head;
}
// 添加一个元素
public void append(Object element) {
Node node = new Node(element);
if(this.head == null) {
this.head = node;
}else {
Node current = (Node) this.head;
while (current.getNext() != null) {
current = (Node) current.getNext();
}
current.setNext(node);
}
this.length++;
}
// 向指定位置插入一个元素
public boolean insert(int position,Object element) {
Node node = new Node(element);
if (position < 0 || position > this.length){
System.err.println("数组下标越界");
return false;
}
if(position == 0) {
Node first = (Node) this.head;
this.head = node;
node.setNext(first);
}else{
Node current = (Node) this.head;
Node pre = null;
int index = 0;
while(index++ < position) {
pre = current;
current = (Node) current.getNext();
}
pre.setNext(node);
node.setNext(current);
}
this.length++;
return true;
}
// 获取对应位置的元素
public Object get(int position) {
if(position < 0 || position > this.length) {
return null;
}
int index = 0;
Node current = (Node) this.head;
while (index++ < position) {
current = (Node) current.getNext();
}
return current == null ? null : current.getElement();
}
// 返回元素在列表中的索引,如果没有就返回-1
public int indexOf(Object element) {
Node current = (Node) this.head;
int index = 0;
while(current.getNext() != null) {
if(current.getElement().equals(element)) {
return index;
}
index++;
current = (Node) current.getNext();
}
if(current.getNext() == null) {
current.getElement().equals(element);
return this.length - 1;
}
return -1;
}
// 删除某个位置的元素
public Object removeAt(int position) {
if (position < 0 || position > this.length - 1) {
System.err.println("下标越界");
return null;
}
if(position == 0) {
Node node = (Node) this.head;
this.head = (Node) node.getNext();
this.length--;
return node.getElement();
}
Node current = (Node)this.head;
Node pre = null;
for (int i = 0; i < position; i++) {
pre = current;
current = (Node) current.getNext();
}
pre.setNext((Node) current.getNext());
this.length--;
return current.getElement();
}
// 修改某个位置的元素
public boolean update(int position,Object newElement) {
// 删除position位置的元素
Object result = this.removeAt(position);
// 在position出插入元素
return result != null? this.insert(position,newElement):false;
}
// 删除指定的元素
public int remove(Object element){
int index = this.indexOf(element);
if(index == -1){
System.err.println(element + "不在链表中");
}else{
this.removeAt(index);
}
return index;
}
// 判断链表是否为空
public boolean isEmpty(){
return this.length <= 0;
}
// 获取链表的元素个数
public int size(){
return this.length;
}
@Override
public String toString() {
return "MyLinkedList{" +
"length=" + length +
", head=" + head +
'}';
}
public static void main(String[] args) {
MyLinkedList list = new MyLinkedList();
list.append(1);
list.append(2);
list.append(3);
list.append(4);
list.update(4,1);
// list.insert(0,29);
/*list.insert(2,9);
list.insert(7,6);*/
// list.update(3,9);
System.out.println(list);
}
}
基于C#实现的单向链表
节点类
/// <summary>
/// 节点类
/// </summary>
class Node
{
public Node() { }
public Node(Object element) {
this.Element = element;
}
// 节点中的元素
public Object Element { get; set; }
//下一个节点的指向
public Object Next { get; set; }
public override string ToString()
{
return "Node{"+
"element: " + Element +
",next: " + Next +
"}";
}
}
单向链表类:
/// <summary>
/// 实现单向链表
/// </summary>
class MyLinkedList
{
// 头节点
public Object Head { get; set; }
// 链表的元素个数
public int Length { get; set; }
public override string ToString()
{
return "MyLinkedList{" +
"length=" + Length +
", head=" + Head +
"}";
}
/// <summary>
/// 添加一个元素
/// </summary>
/// <param name="element">添加的元素</param>
public virtual void Append(Object element) {
// 创建一个节点对象
Node node = new Node(element);
// 判断链表中是否有值
if(this.Head == null)
{
// 没有值,直接将node节点指向头节点
this.Head = node;
}else
{
// 有值的情况
Node current = (Node)this.Head;
while(current.Next != null)
{
current = (Node)current.Next;
}
current.Next = node;
}
// 链表长度+1
this.Length ++;
}
/// <summary>
/// 向指定位置插入一个元素
/// </summary>
/// <param name="position">插入元素的位置</param>
/// <param name="element">插入到元素</param>
/// <returns></returns>
public virtual bool Insert(int position,Object element)
{
// 判断是否越界
if(position < 0 || position > Length)
{
// 实际情况还需要做异常处理
Console.Error.WriteLine("插入位置不合法");
return false;
}
Node node = new Node(element);
// 插入第一个位置
if(position == 0)
{
// 获取头节点
Node firstNode = (Node)this.Head;
this.Head = node;
node.Next = firstNode;
}else
{
// 当前节点
Node current = (Node)this.Head;
// 用于记录current的前一个节点
Node preNode = null;
for(int i = 0;i < position; i++)
{
preNode = current;
current = (Node)current.Next;
}
preNode.Next = node;
node.Next = current;
}
this.Length++;
return true;
}
/// <summary>
/// 根据下标获取对应位置的元素
/// </summary>
/// <param name="position">下标值</param>
/// <returns></returns>
public virtual Object Get(int position)
{
// 判断下标是否越界
if(position < 0||position > this.Length-1)
{
Console.Error.WriteLine("下标不合法");
return null;
}
Node current = (Node)this.Head;
for(int i = 0;i < position; i++)
{
current = (Node)current.Next;
}
return current.Element;
}
/// <summary>
/// 返回元素在列表中的索引,如果没有就返回-1
/// </summary>
/// <param name="element">元素</param>
/// <returns></returns>
public virtual int IndexOf(Object element)
{
Node current = (Node)this.Head;
int index = 0;
while(index < this.Length)
{
if (current.Element.Equals(element))
{
return index;
}
index++;
current = (Node)current.Next;
}
return -1;
}
/// <summary>
/// 根据下标删除元素
/// </summary>
/// <param name="position">删除元素的位置</param>
/// <returns></returns>
public virtual Object RomoveAt(int position)
{
// 判断下标是否合法
if(position < 0||position > this.Length - 1)
{
Console.Error.WriteLine("下标值不合法");
return null;
}
Node current = (Node)this.Head;
if (position == 0)
{
this.Head = current.Next;
this.Length--;
return current.Element;
}
Node prevNode = null;
for(int i = 0;i < position; i++)
{
prevNode = current;
current = (Node)current.Next;
}
prevNode.Next = current.Next;
this.Length--;
return prevNode.Element;
}
/// <summary>
/// 根据下标修改元素
/// </summary>
/// <param name="position"></param>
/// <param name="newElement"></param>
/// <returns></returns>
public virtual bool Update(int position,Object newElement)
{
// 先删除该位置元素
Object result = this.RomoveAt(position);
//再在该位置插入新的元素
return result != null ? this.Insert(position, newElement) : false;
}
/// <summary>
/// 删除元素
/// </summary>
/// <param name="element"></param>
/// <returns>删除的元素下标</returns>
public virtual int Remove(Object element)
{
// 获取下标
int index = this.IndexOf(element);
if (index == -1)
{
Console.Error.WriteLine("没有该元素");
}else
{
// 根据下标删除元素
this.RomoveAt(index);
}
return index;
}
/// <summary>
/// 判断链表是否为空
/// </summary>
/// <returns></returns>
public virtual bool IsEmpty()
{
return this.Length <= 0;
}
/// <summary>
/// 获取元素的个数
/// </summary>
/// <returns></returns>
public virtual int Size()
{
return this.Length;
}
}
双向链表
这里利用继承,在单向链表的基础上实现双向链表。
基于Java实现的双向链表
节点类
class DoubleNode extends Node{
private DoubleNode prev;
public DoubleNode() {
}
public DoubleNode(Object element) {
super(element);
}
public DoubleNode getPrev() {
return prev;
}
public void setPrev(DoubleNode prev) {
this.prev = prev;
}
}
链表类:
class DoubleLinkedList extends MyLinkedList{
private DoubleNode tail;
public Object getTail() {
return tail;
}
public void setTail(DoubleNode tail) {
this.tail = tail;
}
@Override
public String toString() {
return "DoubleLinkedList{" +
"length=" + this.getLength() +
", head=" + this.getHead() +
", tail=" + tail +
'}';
}
@Override
public void append(Object element) {
DoubleNode node = new DoubleNode(element);
if (this.getLength() == 0) {
this.setHead(node);
this.tail = node;
}else {
this.tail.setNext(node);
node.setPrev(this.tail);
this.tail = node;
}
this.setLength(this.getLength()+1);
}
@Override
public boolean insert(int position, Object element) {
DoubleNode node = new DoubleNode(element);
if (position < 0||position > this.getLength()) {
System.err.println("数组下标越界啦");
return false;
}
DoubleNode current = (DoubleNode)this.getHead();
if (position == 0) {
this.setHead(node);
node.setNext(current);
current.setPrev(node);
this.setLength(this.getLength()+1);
return true;
}
if(position == this.getLength()) {
this.append(element);
}
if(position == 0&&this.getLength() == 0) {
this.setHead(node);
this.tail = node;
}else{
for (int i = 0; i < position; i++) {
current = (DoubleNode) current.getNext();
}
current.getPrev().setNext(node);
node.setNext(current);
current.setPrev(node);
}
this.setLength(this.getLength()+1);
return true;
}
@Override
public Object removeAt(int position) {
if (position <0 || position > this.getLength()-1) {
System.err.println("数组下标越界啦!");
return false;
}
DoubleNode current = (DoubleNode) this.getHead();
if (position == 0&&this.getLength() == 0) {
this.setHead(null);
this.tail = null;
this.setLength(this.getLength()-1);
return current.getElement();
}else if (position == this.getLength()-1){
// 获取尾节点
DoubleNode tailNode = this.tail;
tailNode.getPrev().setNext(null);
this.tail = tailNode.getPrev();
this.setLength(this.getLength()-1);
return tailNode.getElement();
}else if(position == 0) {
this.setHead(current.getNext());
((DoubleNode)current.getNext()).setPrev(null);
this.setLength(this.getLength()-1);
return current.getElement();
}
for (int i = 0; i < position; i++) {
current = (DoubleNode) current.getNext();
}
DoubleNode prev = current.getPrev();
prev.setNext(current.getNext());
DoubleNode n = (DoubleNode)current.getNext();
n.setPrev(prev);
this.setLength(this.getLength()-1);
return current.getElement();
}
public static void main(String[] args) {
DoubleLinkedList list = new DoubleLinkedList();
list.append(1);
list.append(2);
list.append(3);
// list.insert(0,9);
/* System.out.println(list.get(3));
System.out.println(list.indexOf(9));*/
Object o = list.removeAt(1);
System.out.println(o);
// System.out.println(list.update(1, 20));
// list.update(3,20);
System.out.println(list);
}
}
基于C#实现的双向链表
节点类
/// <summary>
/// 继承自Node
/// </summary>
class DoubleNode:Node
{
public DoubleNode() { }
public DoubleNode(Object element)
{
this.Element = element;
}
// 存放当前节点前一个节点的引用
public Object Prev { get; set; }
}
链表类:
/// <summary>
/// 继承自MyLinkedList
/// </summary>
class DoubleLinkedList:MyLinkedList
{
// 尾节点
public Object Tail { get; set; }
public override string ToString()
{
return "DoubleLinkedList{" +
"length=" + Length +
", head=" + Head +
", tail=" + Tail +
"}";
}
/// <summary>
/// 向链表中追加元素
/// </summary>
/// <param name="element">元素</param>
public override void Append(Object element)
{
DoubleNode node = new DoubleNode(element);
// 链表为空
if(this.Length == 0)
{
this.Head = node;
this.Tail = node;
}else
{
// 链表不为空,获取尾部节点
DoubleNode tailNode = (DoubleNode)this.Tail;
tailNode.Next = node;
node.Prev = tailNode;
this.Tail = node;
}
this.Length++;
}
///
public override bool Insert(int position, object element)
{
DoubleNode node = new DoubleNode(element);
//判断下标是否越界
if(position < 0||position > this.Length)
{
Console.Error.WriteLine("下标越界");
return false;
}
//链表中没有元素
if(this.Length == 0)
{
this.Head = node;
this.Tail = node;
this.Length++;
return true;
}
// 插入到头部
if(position == 0)
{
DoubleNode headNode = (DoubleNode)this.Head;
this.Head = node;
node.Next = headNode;
headNode.Prev = node;
this.Length++;
return true;
}
// 插入到尾部
if(position == this.Length)
{
this.Append(element);
return true;
}
// 插入到中间
DoubleNode current = (DoubleNode)this.Head;
for (int i = 0; i < position; i++)
{
current = (DoubleNode)current.Next;
}
// 获取当前位置的前一个节点
DoubleNode prevNode = (DoubleNode)current.Prev;
prevNode.Next = node;
node.Prev = prevNode;
node.Next = current;
current.Prev = node;
this.Length++;
return true;
}
public override object RomoveAt(int position)
{
// 判断下标是否合法
if(position < 0 ||position > this.Length-1)
{
Console.Error.WriteLine("下标不合法");
return null;
}
// 删除头部元素
if(position == 0)
{
DoubleNode headNode = (DoubleNode)this.Head;
headNode.Prev = null;
this.Head = headNode.Next;
this.Length--;
return headNode.Element;
}
// 删除尾部元素
if (position == this.Length - 1)
{
DoubleNode tailNode = (DoubleNode)this.Tail;
// 获取尾部节点的上一个节点
DoubleNode prev = (DoubleNode)tailNode.Prev;
prev.Next = null;
this.Tail = prev;
this.Length--;
return tailNode.Element;
}
DoubleNode current = (DoubleNode)this.Head;
// 删除中间的元素
for(int i = 0;i < position; i++)
{
current = (DoubleNode)current.Next;
}
// 获取当前节点的上一个节点
DoubleNode prevN = (DoubleNode)current.Prev;
// 获取当前节点的下一个节点
DoubleNode nextN = (DoubleNode)current.Next;
prevN.Next = nextN;
nextN.Prev = prevN;
this.Length--;
return current.Element;
}
}