zoukankan      html  css  js  c++  java
  • 第三章 链表

    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实现单链表常见操作

    韩顺平数据结构

    大话数据结构

  • 相关阅读:
    long和Long的区别
    C语言的变量的内存分配
    Java蓝桥杯 算法提高 九宫格
    Java实现 蓝桥杯算法提高金明的预算方案
    Java实现 蓝桥杯 算法提高 新建Microsoft world文档
    Java实现 蓝桥杯 算法提高 快乐司机
    Java实现 蓝桥杯 算法提高 三角形
    Java实现 蓝桥杯 算法提高 三角形
    Java实现 蓝桥杯 算法提高 三角形
    Java实现 蓝桥杯 算法提高 三角形
  • 原文地址:https://www.cnblogs.com/victorxiao/p/13580465.html
Copyright © 2011-2022 走看看