赞
踩
链表又叫做线性表的链式存储,有一个个节点组成,类似生活中火车车厢一节一节串在一起。链表是一种物理存储结构上非连续的存储结构,数据元素的逻辑顺序是通过链表的引用链接次序实现的。
链表中的每个结点包含两部分:一个叫数据域:存储数据元素信息;另一个叫指针域:存储下一个结点的存储地址。
链表根据是否带有头结点,不带头结点,单向或者双向,循环或者非循环可以组成8种情况:
带头 双向 循环链表 不带头 双向 循环链表
带头 双向 非循环链表 不带头 双向 非循环链表
带头 单向 循环链表 不带头 单向 循环链表
带头 单向 非循环链表 不带头 单向 非循环链表
其中双向是带有两个指针域,一个指针域是指向下个结点的存储地址,另一个指针域指向上一个结点的存储地址。
重点掌握:带头单向非循环链表和不带头双向非循环链表
(1)创建链表
- static class ListNode { //静态内部类
- public int value;
- public ListNode next; //因为next是指向结点的,所以是ListNode类型
-
- public ListNode(int value) { //实例化结点的时候不知道next指向谁,所以不用next构造
- this.value = value;
- }
- }
-
- //链表的属性,链表的头结点
- //不能创建在内部类里,否则成为新结点的头结点
- public ListNode head; //null
-
- public void creatList() { //方法执行完后,里面的局部变量node1...都会被回收掉,链表还存在
- ListNode node1 = new ListNode(12);
- ListNode node2 = new ListNode(23);
- ListNode node3 = new ListNode(34);
- ListNode node4 = new ListNode(45);
-
- node1.next = node2;
- node2.next = node3;
- node3.next = node4;
-
- this.head = node1;
- }
(2)遍历打印链表元素
重点:1. 怎么从第一个结点走到下个结点-------head = head.next
2. 什么时候算把所有结点遍历完成 --------head==null
- public void display() {
- ListNode cur = head; //定义一个cur代替head去遍历
- while (cur != null) { //指向空就退出循环
- System.out.println(cur.value + " ");
- cur = cur.next; //从当前结点走到下个结点
- }
- }
(3)查看关键字key是否在单链表中
- public boolean contains(int key) {
- ListNode cur = head; //定义一个cur代替head去遍历
- while (cur != null) { //循环执行完后cur指向尾结..点的后一个位置,也就是null
- if (cur.value == key) {
- System.out.println("元素存在");
- return true;
- }
- cur = cur.next;
- }
- return false;
- }
(4)求当前链表有多少个结点
- public int size() {
- int count = 0;
- ListNode cur = head; //定义一个cur代替head去遍历
- while (cur != null) {
- count++;
- cur = cur.next;
- }
- return count;
- }
(5)头插法
- public void addFirst(int data) {
- ListNode node = new ListNode(data); //创建一个新结点
- node.next = this.head; //先让新节点的next指向head,然后head指针走向node
- this.head = node;
- }
(6)尾插法
- public void addLast(int data) {
- ListNode node = new ListNode(data); //创建一个新结点
-
- if (head == null) {
- head = node; //分两种情况,如果链表是空的
- } else {
- ListNode cur = head; //定义一个cur代替head去遍历
-
- while (cur.next != null) { //cur.next指向空的时候,就说明是最后一个结点
- cur = cur.next;
- }
- cur.next = node;
- }
- }
(6)链表的插入
1.先找到cur的位置 : cur先走到Index - 1步
- public void addIndex(int index, int data) throws indexException {
- //判断index是否合法
- if (index < 0 || index > size()) {
- throw new indexException("index不合法: " + index);
- }
- ListNode node = new ListNode(data);
- if (head == null) { //链表为空
- head = node;
- return;
- }
- //如果索引为0,直接头插法
- if (index == 0) {
- addFirst(data);
- return;
- }
-
- //如果索引为链表长度,直接尾插法
- if (index == size()) {
- addLast(data);
- return;
- }
-
- //中间插入
- ListNode cur = searchPrevIndex(index); //cur指向index前一个位置
- node.next = cur.next; //先把要插入的结点node的next指向cur的next,先绑定后面
- cur.next = node; //cur的next再指向node,不然会丢失
-
- }
(7) 查看关键字key是否在链表中
- public boolean contains(int key) {
- ListNode cur = head; //定义一个cur代替head去遍历
- while (cur != null) { //循环执行完后cur指向尾结..点的后一个位置,也就是null
- if (cur.value == key) {
- System.out.println("元素存在");
- return true;
- }
- cur = cur.next;
- }
- return false;
- }
(8) 删除key结点
因为是单向链表,cur不能走到要删除结点的位置,否则找不到删除结点的前驱,因为链表的删除操作是让删除结点的前驱直接指向删除结点的后继,所以cur要走到要删除结点的前驱即可.
- public void remove(int key) {
- //链表为空
- if (head == null) {
- return;
- }
- //删除的关键字是头结点
- if (head.value == key) {
- head = head.next; //直接指向头结点的下一位
- return;
- }
- //删除操作
- ListNode cur = findPrevKey(key);
- if (cur == null) {
- System.out.println("没有你要删除的数字");
- return;
- } else {
- cur.next = cur.next.next; //直接指向要删除的下下个结点
- }
- }
-
- private ListNode findPrevKey(int key) { //找322去 到要删除结点的前驱才能
- ListNode cur = head; //定义一个cur代替head去遍历
- while (cur.next != null) {
- if (cur.next.value == key) {
- return cur;
- } else {
- cur = cur.next;
- }
- }
- return null;
- }
(9) 删除所有值为key的节点
- public void removeAllKey(int key) {
- if (head == null) {
- return;
- }
-
- while (head.value == key) { //会出现重复要删除的头结点,所以要用循环判断
- head = head.next;
- return;
- }
- ListNode cur = head.next; //定义一个cur代替head去遍历
- ListNode prev = head; //代表要删除结点的前驱
- while (cur != null) {
- if (cur.value == key) {
- prev.next = cur.next; //这一步cur的前驱直接跳过cur结点,指向cur的后继
- cur = cur.next;
- } else {
- prev = cur;
- cur = cur.next;
- }
- }
- }
(10) 求链表的长度
- public int size() {
- int count = 0;
- ListNode cur = head; //定义一个cur代替head去遍历
- while (cur != null) {
- count++;
- cur = cur.next;
- }
- return count;
- }
(11) 清空链表
当一个对象没有人引用的时候就会被回收掉
- //清空
- @Override
- public void clear() {
- head = null; //当一个对象没人引用的时候就会被回收
- }
优点:
1、对于频繁插入和删除数据的操作,链表的效率很高
2、单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制
缺点:
1、在查找数据元素上,时间复杂度为O(N)
2、链表不是一种随机存储结构,不能随机存取元素。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。