链表

来源:互联网 发布:java程序员工作职责 编辑:程序博客网 时间:2024/06/05 19:15

什么是链表

链表是一种用于存储数据集合的数据结构。链表有以下属性:
- 相邻元素之间通过指针连接
- 最后一个元素的后继指针值为null
- 在程序执行过程中,链表的长度可以增加或者缩小
- 链表的空间能够按需分配(直到系统内存耗尽)
- 没有内存空间的浪费(但是链表中的指针需要一些额外的内存开销)

链表抽象数据类型

链表抽象数据类型中的操作如下:

链表的主要操作
  • 插入:插入元素到链表中
  • 删除:移除并返回链表中指定位置的元素
    链表的辅助操作:
  • 删除链表:移除链表中的所以元素(清空链表)
  • 计数:返回链表中元素的个数
  • 查找:寻找从链表表尾开始的第n个结点(node)

链表 VS 数组

数组:
  • 整个数组所有的元素都存储在操作系统分配的一个内存块中。
  • 通过使用特定元素的索引作为数组下标,可以在常数时间内访问数组元素。
数组优点:
  • 简单且易用
  • 访问元素块(常数时间)
数组的缺点:
  • 大小固定:数组的大小是静态的(在使用前指定数组的大小)
  • 分配一个连续空间块:数组初始分配空间时,有时无法分配能存储整个数组的内存空间(当数组规模太大时)
  • 基于位置的插入操作实现复杂:如果要在数组中的给定位置插入元素,可能需要移动存储在数组中的其他元素,这样才能腾出指定的位置来放插入的新元素。如果在数组的开始位置插入元素,那么移动操作的开销将更大。
动态数组: 了解这种概念

动态数组是一种可随机存取且可自动调整大小的线性数据结构,能够添加或删除元素。
实现动态数组的一个简单方法是,首先初始化固定大小的数组。一旦数组存储满了,创建一个两倍于原始数组大小的新数组。同样,若数组中存储的元素个数小于数组大小的一半,则把数组大小减少一半。

链表的优点
  • 常数时间内扩展
  • 初始时只需要分配一个元素的存储空间
链表的缺点
  • 访问单个元素的时间开销较大(数组是随机存取,即存取数组中任一元素的时间开销为O(1),而链表最差为O(n))
  • 存储和检索数据的开销方面却有很大的不足。有时很难对链表操作。如果要删除最后一项,倒数第二项必须更改后继指针值为NULL。这需要从头遍历链表,找到倒数第二个结点的链接,并设置其后继指针为NULL
  • 链表中的额外指针引用需要浪费内存

单向链表

链表通常是指单向链表,它包含多个结点,每个结点有一个指向后继元素的next指针。表中最后一个结点的next指针值为null,表示该链表的结束。
链表的类型声明:

public class ListNode {    private int data;    private ListNode next;    public ListNode(int data){        this.data = data;    }    public int getData() {        return data;    }    public void setData(int data) {        this.data = data;    }    public ListNode getNext() {        return next;    }    public void setNext(ListNode next) {        this.next = next;    }}
1.链表的基本操作
  • 遍历链表
  • 在链表中插入一个元素
  • 在链表中删除一个元素
2.链表的遍历

假设表头结点的指针指向链表中的第一个结点。遍历链表需完成以下步骤:
- 沿指针遍历
- 遍历时显示结点的内容(或计数)
- 当next指针的值为NULL时结束遍历

统计链表中结点的个数

int ListLength(ListNode headNode){    int length = 0;    ListNode currentNode = headNode;    while(currentNode != null){        length++;        currentNode = currentNode.getNext();    }    return length;}
3.单向链表的插入

单向链表的插入操作可分为以下3中情况:
- 在链表的表头前插入一个新结点(链表开始处)
- 在链表的表尾后插入一个新结点(链表结尾处)
- 在链表的中间插入一个新结点(随机位置)

3.1在单向链表的开头插入结点
  • 更新新节点next指针,使其指向当前的表头结点
  • 更新表头指针的值,使其指向新结点
3.2在单向链表的结尾插入结点
  • 新结点的next指针指向NULL
  • 最后一个结点的next指针指向新结点
3.3在单向链表的中间插入结点
  • 如果要在位置3增加一个元素,则需要将指针定位于链表的位置2.即需要从表头开始进过两个结点,然后插入新结点。为了简单起见,假如第二个结点为位置结点,新结点的next指针指向位置结点的下一个结点
  • 位置结点的next指针指向新结点
public ListNode InsertInLinkedList(ListNode headNode,ListNode nodeToInsert,int position){    if(headNode == null){//如果链表为空,插入操作:返回要插入的结点        return nodeToInsert;    }    int size = ListLength(headNode);    if(position > size+1 || position < 1){        System.out.println("Position of node to insert is invalid.The valid inputs are 1 to"+(size+1));        return headNode;    }    if(position == 1){//在链表开头插入        nodeToInsert.setNext(headNode);        return nodeToInsert;    }else{//在链表中间或末尾插入        ListNode previousNode = headNode;        int count = 1;        while(count < position-1){            previousNode = previousNode.getNext();            count++;        }        ListNode currentNode = previousNode.getNext();        nodeToInsert.setNext(currentNode);        previousNode.setNext(nodeToInsert);    }    return headNode;}
单向链表的删除
删除单向链表的第一个结点
  • 创建一个临时结点,它指向表头指针所指的结点
  • 修改表头指针的值,使其指向下一个结点,并移除临时结点
删除单向链表的最后一个结点
  • 遍历链表,在遍历时还要保存前驱结点的地址。当遍历到链表的表尾时,将有两个指针,分别是表尾节点的指针tail及指向表尾结点的前驱结点的指针
  • 将表尾的前驱结点的next指针更新为NULL
  • 移除表尾结点
删除单向链表中间的一个结点
  • 与上一种删除情况类似,在遍历时保存前驱结点的地址。一旦找到要被删除的结点,将前驱结点next指针的值更新为被删除结点的next 指针的值。
  • 移除需删除的当前结点。
//单向链表的删除public ListNode DeleteNodeFromLinkedList(ListNode headNode,int position){    int size = ListLength(headNode);    if(position > size || position < 1){        System.out.println("Position of node to delete is invalid.The valid inputs are 1 to " + size);        return headNode;    }    if(position == 1){//删除单向链表的表头结点        ListNode currentNode = headNode.getNext();        headNode = null;        return currentNode;    }else{        ListNode previousNode = headNode;        int count = 1;        while(count<position){            previousNode = previousNode.getNext();            count++;        }        ListNode currentNode = previousNode.getNext();        previousNode.setNext(currentNode.getNext());        currentNode = null;    }    return headNode;}   
删除单向链表

将当前结点存储在临时变量中,然后释放当前结点空间的方式来完成。当释放完当前结点后,移动到下一个结点并将其存储在零食变量中,然后不断重复该过程直至释放所有结点

void DeleteLinkedList(ListNode head){    ListNode auxilaryNode,iterator = head;    while(iterator !=null){        auxilaryNode = iterator.getNext();        iterator = null ;//在Java中,垃圾回收器将自动处理    }}
0 0
原创粉丝点击