线性表-4.单向链表增删改查的理解

231 阅读5分钟

单向链表的概念

  • 链表中每个数据的存储都由以下两部分组成:数据元素本身,其所在的区域称为数据域;指向直接后继元素的指针,所在的区域称为指针域数据域和指针域结合起来就是一个节点

  • 链表的各个节点不一定是连续存储,不像顺序存储结构比如数组;

  • 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定;

上图结构在链表中称为节点。也就是说,链表实际存储的是一个一个的节点,真正的数据元素包含在这些节点中的数据域中

单向链表构成的结构如上图所示。

头节点,首元节点

其实,上图图所示的链表结构并不完整。一个完整的链表需要由以下几部分构成:

节点:链表中的节点又细分为头节点、首元节点和其他节点:

头节点:其实就是一个不存任何数据的空节点,通常作为链表的第一个节点。

头结点中的next指针域存放的是第一个有数据的节点(首元节点)在内存中的地址

一般来说,设置一个头结点,指向首元节点,即head.next==firstNode对于链表来说,头节点不是必须的,它的作用只是为了方便解决某些实际问题;

首元节点:由于头节点(也就是空节点)的缘故,链表中称第一个存有数据的节点为首元节点。首元节点只是对链表中第一个存有数据节点的一个称谓,没有实际意义;

其他节点:链表中其他的节点;

因此,一个存储 {1,2,3} 的完整链表结构如下图所示:

Java代码思路分析

首先有一个节点类,用来存放数据和指针域next.

其次建立一个单向链表类,表示一个链表,链表的本质就是一个个节点串联而成形成的线性表。 单向链表类一方面存储着节点数据,另一方面对该链表中的数据节点进行增删改查等操作。

对于增删改查的操作: 基本思路都是通过一个临时节点变量,初始赋值为head头节点,然后进行循环遍历,**找到对应的节点位置,关键点是把握好要增删改的节点的前一个和后一个节点。**对前后节点的指针指向进行重新赋值,达到增删改查的操作。

代码示例

package com.ethan.linkedlist;

import org.junit.Test;

public class SingleLinkedListDemo {
	//LinkedList
	
	@Test
	public void SingleLinkedListTest() {
		//创建几个英雄节点
		HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
		HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
		HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
		HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
		
		
		//创建初始化单向链表
		SingleLinkedList linkedList = new SingleLinkedList();
		
//		linkedList.add(hero1);
//		linkedList.add(hero4);
//		linkedList.add(hero3);
//		linkedList.add(hero2);
		
		linkedList.addByOrder(hero1);
		linkedList.addByOrder(hero4);
		linkedList.addByOrder(hero3);
		linkedList.addByOrder(hero2);
		System.out.println("########按顺序添加英雄列表#########");
		linkedList.showList();
		
		HeroNode hero5 = new HeroNode(4, "李逵", "黑旋风");
		linkedList.update(hero5);
		System.out.println("########更新某个英雄列表#########");
		linkedList.showList();
		System.out.println("########删除编号为no的英雄列表#########");
		linkedList.delete(4);
		linkedList.showList();
		
	}
	
}

class SingleLinkedList{
	//初始化一个头结点,不存储数据
	HeroNode head = new HeroNode(0,null,null);
	
	//向链表末尾添加新节点
	public void add(HeroNode node) {
		HeroNode temp = head;
		while(true) {
			if(temp.next==null) {
				break;
			}
			temp = temp.next;
		}
		temp.next = node;
	}
	
	//第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
	public void addByOrder(HeroNode node) {
		HeroNode temp = head;
		boolean flag = false;//标志链表内是否有同样编号的节点
		while(true) {
			if(temp.next==null) {//遍历到了最后一个节点,退出
				break;
			}
			if(temp.next.no > node.no) {
				//假设插入节点为C,如果遍历找到的中间某节点A的下一个节点B的编号比我们插入的英雄节点编号大,
				//那么我们就找到了要插入节点C的前一个节点为A,确定此时的temp=A
				break;
			}else if(temp.next.no == node.no){
				//如果遍历找到的中间某节点A的下一个节点B的编号比我们插入的英雄节点编号相等
				//那么无法将该节点插入到链表中,已存在该排名
				flag=true;
				break;
			}
			temp = temp.next;
		}
		
		if(flag) {
			System.out.printf("该英雄编号[%d]已存在",node.no);
		}else {
			//最关键的步骤,先将要插入的节点C的next指针指向B,然后再将A节点的next指针指向C,顺序不能变
			node.next = temp.next;
			temp.next = node;
		}
	}
	
	
	//修改英雄节点的数据(名字或者绰号,根据编号来修改
	public void update(HeroNode node) {
		//首先先判断链表是不是空的
		if(head.next ==null) {
			System.out.println("链表为空,无法修改");
			return ;
		}
		HeroNode temp = head.next;
		boolean flag = false;//标志链表内是否有同样编号的节点
		while(true) {
			if(temp==null) {//此时temp已经是最后一个元素的后面的null,表示已经遍历完链表了,退出
				break;
			}
			if(temp.no == node.no) {
				flag = true;
				break;
			}
			temp = temp.next;
		}
		
		if(flag) {
			temp.name=node.name;
			temp.nickName=node.nickName;
		}else {
			System.out.printf("链表内无[%d]节点!",node.no);
		}
	}
	
	//删除英雄节点的数据(根据编号来删除
	public void delete(int no) {
		//首先先判断链表是不是空的
		if(head.next ==null) {
			System.out.println("链表为空,无法修改");
			return ;
		}
		HeroNode temp = head;
		boolean flag = false;//标志链表内是否有同样编号的节点
		while(true) {
			if(temp.next==null) {//遍历到最后一个节点
				if(temp.no == no) {
					flag = true;
				}
				break;
			}
			if(temp.next.no==no) {
				flag = true;
				break;
			}
			temp = temp.next;
		}
		
		if(flag) {//找到了要删除的节点的前一个节点temp
			//将要删除的节点C的前一个节点A指向C的下一个节点B即可
			temp.next=temp.next.next;
		}else {
			System.out.printf("链表内无[%d]节点!",no);
		}
	}
	
	//遍历打印链表内的数据
	public void showList() {
		//先判断链表是否为空
		if(head.next==null) {
			System.out.println("链表为空~~~");
			return ;
		}
		//头节点无数据,又无法移动,让一个中间变量进行辅助遍历
		//因为链表不为空,最起码有一个节点
		HeroNode temp = head.next;
		while(true) {
			//首先判断是否到链表尾
			if(temp == null) {
				break;
			}
			System.out.println(temp);
			temp = temp.next;
		}
	}
	
}


class HeroNode{
	
	public int no;//排名编号
	public String name;//名字
	public String nickName;//绰号
	public HeroNode next;//下一个节点
	
	public HeroNode() {
		
	}

	public HeroNode(int no, String name, String nickName) {
		this.no = no;
		this.name = name;
		this.nickName = nickName;
	}

	@Override
	public String toString() {
		return "HeroNode [no=" + no + ", name=" + name + ", nickName=" + nickName  + "]";
	}
	
}