• 第三章 链表


    1 基本概念

    • 链表是一系列存储数据元素的单元通过指针串接起来形成的,因此每个单元至少有两个域,一个域用于数据元素的存储,另一个或两个域是指向其他单元的指针。这里具有一个数据域和多个指针域的存储单元通常称为结点(node)
    • 链表分为带头结点的链表和不带头结点的链表,根据实际需要来确定。
    • 指向链表中第一个结点的指针称为头指针,头指针是链表必须的元素;
    • 链表数据结构中主要包含单向链表、双向链表及循环链表

    2 概念辨析:头结点,头指针

    • 通常使用“头指针”来标识一个链表,头指针始终指向链表的第一个结点。如单链表L,头指针为NULL的时表示一个空链表。下图为一个不带头结点的单链表,头指针指向链表第一个结点,但结点1并不是头结点

      image-20200826141721487

    • 在单链表的第一个结点之前附加一个结点,称为头结点。头结点的Data域可以不存储任何信息,也可以记录表长等相关信息。如下图,就是一个含有头结点的链表,此时头指针指向头结点

    image-20200826140121285

    • 无论是否有头结点,头指针始终指向链表的第一个结点。如果有头结点,头指针就指向头结点。

    3 单链表

     单链表只有一个指针域,在整个结点中数据域用来存储数据元素,指针域用于指向下一个具有相同结构的结点,如下图所示。

    img

     与数组类似,单向链表中的节点也具有一个线性次序。如下图所示,如果节点 1 的 next 引用指向节点2,则结点1就是结点2的直接前驱,结点2是结点1的直接后继。即只能通过前驱节点找到后继节点,而无法从后继节点找到前驱节点。

    image-20200826141721487

    特点:

    • 数据元素的存储对应的是不连续的存储空间,每个存储结点对应一个需要存储的数据元素。每个结点是由数据域和指针域组成。 元素之间的逻辑关系通过存储节点之间的链接关系反映出来。
    • 逻辑上相邻的节点物理上不必相邻。

    优点:

    • 插入、删除灵活 。不必移动节点,只要改变节点中的指针,但是需要先定位到结点上。
    • 有元素才会分配结点空间,不会有闲置的结点。

    缺点:

    • 比顺序存储结构的存储密度小 。每个节点都由数据域和指针域组成,所以相同空间内假设全存满的话顺序比链式存储更多。
    • 查找结点时链式存储要比顺序存储慢。每个节点地址不连续、无规律,导致按照索引查询效率低下。

    单链表的Java实现

    package com.victor.linkedlist;
    
    import java.util.Scanner;
    
    
    public class SingleLinkedListDemo {
    
    	public static void main(String[] args) {
    		SingleLinkedList  sll = new SingleLinkedList();
    		char key = ' '; //接收用户输入
    		Scanner scanner = new Scanner(System.in);
    		boolean loop = true;
    		//输出一个菜单栏
    		while(loop){
    			System.out.println("s(show): 打印链表");
    			System.out.println("a(add): 从尾部添加结点");
    			System.out.println("g(get): 删除尾结点");
    			System.out.println("l(head): 输出链表长度");
    			System.out.println("e(exit): 退出程序");
    			key = scanner.next().charAt(0);
    			switch (key) {
    			case 's':
    				sll.showLinkedList();
    				break;
    			case 'a': //从尾部添加结点
    				System.out.println("请输入一个整数");
    				int value = scanner.nextInt();
    				sll.addFromTail(value);  //最好判断一下value是不是整数
    				break;
    			case 'g': //删除链表尾结点
    				try {
    					int res = sll.getListNode();;
    					System.out.printf("删除的结点值为%d
    ", res);
    				} catch (Exception e) {
    					System.out.println(e.getMessage()); 
    				}
    				break;
    			case 'l': //输出链表长度
    				System.out.printf("链表长度为%d
    ", sll.getLength());
    				break;
    			case 'e': //退出
    				scanner.close();
    				loop = false;
    				break;
    			default:
    				break;
    			}
    		}
    		System.out.println("程序退出");
    	}
    }
    
    //定义结点类
    class ListNode{
    	private int data;
    	private ListNode next = null;
    	
    	//构造方法
    	public ListNode(int data) {
    		this.data = data;
    	}
    	
    	//返回data值
    	public int getData() {
    		return this.data;
    	}
    
    	//设置data值
    	public void setData(int data) {
    		this.data = data;
    	}
    
    	//返回下一个结点地址
    	public ListNode getNext() {
    		return this.next;
    	}
    
    	//设置下一个结点地址
    	public void setNext(ListNode next) {
    		this.next = next;
    	}
    
    	//重写toString方法
    	@Override
    	public String toString() {
    		return "ListNode [data=" + data + "]";
    	}
    }
    
    //定义单链表类
    class SingleLinkedList{
    	//头结点
    	private ListNode head;
    	
    	//构造方法
    	public SingleLinkedList() {
    		head = new ListNode(-1);
    	}
    	
    	//头插法添加结点
    	public void addFromHead(int data) {
    		ListNode ListNode = new ListNode(data);  //新建结点
    		ListNode curr = head.getNext();
    		head.setNext(ListNode);
    		ListNode.setNext(curr);
    	}
    	
    	//尾插法添加结点
    	public void addFromTail(int data) {
    		ListNode ListNode = new ListNode(data);  //新建结点
    		ListNode curr = head;
    		while(curr.getNext() != null) {
    			curr = curr.getNext();
    		}
    		curr.setNext(ListNode);
    	}
    	
    	//删除链表尾结点
    	public int getListNode() {
    		if (head.getNext() == null) {
    			throw new RuntimeException("链表为空链表");
    		}
    		ListNode curr = head;
    		ListNode prev = head;
    		while(curr.getNext() != null) {
    			prev = curr;
    			curr = curr.getNext();
    		}
    		prev.setNext(null);
    		return curr.getData();
    	}
    	
    	//求链表长度
    	public int getLength() {
    		int length = 0;
    		ListNode curr = head;
    		while(curr.getNext() != null) {
    			length++;
    			curr = curr.getNext();
    		}
    		return length;
    	}
    	
    	//打印链表
    	public void showLinkedList() {
    		ListNode curr = head.getNext();
    		while(curr != null) {
    			System.out.println(curr);
    			curr = curr.getNext();
    		}
    	}
    
    }
    
    

    reference

    深刻理解:带头结点和不带头结点的区别 使用头结点的优势https://blog.csdn.net/qq_24118527/article/details/81317410

    链表详解(易懂)https://blog.csdn.net/SlimShadyKe/article/details/89503062

    详细实现单链表的基本操作【Java版】

    java实现单链表常见操作

    韩顺平数据结构

    大话数据结构

  • 相关阅读:
    部署LVS-DR(直接路由)群集
    快速了解LVS负载均衡群集及部署LVS-NAT(地址转换)群集
    MySQL一些常用的高级SQL语句
    MySQL高可用群集MHA部署及故障测试
    MySQL的主从复制与读写分离
    你知道数据库中数据丢失的严重性吗,快来学习数据库的备份与恢复!!
    快速掌握mysql索引、事务与存储引擎知识以及使用命令
    MySQL数据库常用的一些管理操作
    带大家简单了解MySQL数据库
    剑指offer63-数据流中的中位数**
  • 原文地址:https://www.cnblogs.com/victorxiao/p/13580465.html
Copyright © 2020-2023  润新知