链表

什么是链表

链表是一种用于存储数据集合的数据结构。链表有以下属性:
- 相邻元素之间通过指针连接
- 最后一个元素的后继指针值为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中,垃圾回收器将自动处理
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CiBa-YAO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值