数据结构—链表之单向链表
前言
本文主要介绍数据结构之单向链表的实现思路和代码用例。
提示:以下是本篇文章正文内容
单向链表
一、单向链表介绍
基本概念 : 链表一种线性的数据结构,通过指针将一个个零散的内存块连接起来,链表的每个内存块称为结点。
通过概念我们可以知道一下几点
- 链表也是一种线性结构,之前介绍过的线性结构有数组和队列,链表也是其中之一
- 链表是内存不连续的,是通过指针将内存块(节点)链接起来的
- 不连续的内存相比数组来说好处就是插入或者删除元素的时候(数组尾部操作忽略),不会移动后面所有元素保持内存结构连续性,所以他的插入和删除的时间复杂度是O(1),当然遍历的时间复杂度是O(n),增删比较快
通过链表的概念推断出来以上几点,下面介绍下使用JAVA实现单向链表的实现思路。
二、单向链表实现思路
1.实现思路
假如需要按照分数由高到低存储学生的学号和成绩,使用链表实现的话怎么做,先来一张链表的图
从图中可以看到
- 链表每个节点都是由数据和指针组成的,如果想要实现一个链表,首先需要有节点,这里的节点就是学生的学号,姓名,分数信息,同时每个节点持有一个next指针
- 有了节点怎么创建链表呢?从图中看到,这个链表首先是有一个头节点,每新增一个节点,前面一个节点的指针按照分数与当前节点(currNode)的下一个节点的分数做比较,然后决定是插入到当前节点之后还是当前节点下一个节点之后,这个可以通过遍历整个链表来实现,比较简单,当需要新增节点时,从链表头部(head)开始遍历,head的下一个节点如果为空,说明是空链表,直接把当前插入的节点赋值给head.next即可;如果head.next不为空,插入的学生节点的分数与head.next的节点的分数进行比较,如果插入的节点分数高,就把head.next指向插入的节点,把插入节点的指针指向之前head.next的节点即可,依次类推即可实现
- 删除节点与插入节点十分相似,这里是根据学生编号进行删除,可以参考插入节点的实现方式
- 遍历链表,直接循环打印即可
2.代码示例
package link;
import org.apache.commons.lang.StringUtils;
public class MySingleLinkedTest {
public static void main(String[] args) {
StudentNode s1 = new StudentNode(1,"小明",90);
StudentNode s2 = new StudentNode(2,"小红",80);
StudentNode s3 = new StudentNode(3,"小蓝",70);
StudentNode s4 = new StudentNode(4,"小张",60);
StudentNode s5 = new StudentNode(5,"小绿",60);
StudentNode s6 = new StudentNode(6,"小黑",100);
MySingleLinked singleLinked = new MySingleLinked();
singleLinked.addNode(s1);
singleLinked.addNode(s3);
singleLinked.addNode(s2);
singleLinked.addNode(s5);
singleLinked.addNode(s6);
singleLinked.addNode(s4);
singleLinked.showLinked();
StudentNode studentNode = singleLinked.deleteNode(5);
System.out.printf("删除的链表节点 : %s \n",studentNode.toString());
System.out.println("删除之后的链表 : ");
singleLinked.showLinked();
//再次删除
singleLinked.deleteNode(5);
}
}
class MySingleLinked{
public StudentNode head = new StudentNode(0,"head",0);
/**
* 链表打印
*/
public void showLinked(){
if (null == head.next){
System.out.println("链表为空!");
}
StudentNode studentNode = head.next;
while(true){
System.out.println(studentNode);
if(null == studentNode.next){
break;
}else{
studentNode = studentNode.next;
}
}
}
/**
* 新增节点
* 按照分数节点由高到底插入
*/
public void addNode(StudentNode student){
//基本参数校验
if(null == student){
throw new RuntimeException("入参节点非法!");
}
//基本参数校验
if(student.getScore() < 0
|| StringUtils.isBlank(student.getName())
|| student.getNo() < 0){
throw new RuntimeException("入参节点学生信息的数据为空!");
}
StudentNode currNode = head;//当前节点
while(true){
if(null == currNode.next){//循环到链表尾部,直接添加
currNode.next = student;
break;
}
//单向链表只能在当前位置的后面添加,按分数比较需要与当前节点后面节点去比较
if(student.getScore() > currNode.next.getScore()){
//如果把下面两行代码交换下位置可以跑跑试试,比较有意思
student.next = currNode.next;
currNode.next = student;
break;
}else{
currNode = currNode.next;
}
}
}
/**
* 删除制定序号的节点,并返回该节点
* 与新增类似,这里使用两个指针实现,也可以与新增一样使用单指针实现
*/
public StudentNode deleteNode(int no){
//基本校验
if(null == head.next){
throw new RuntimeException("链表为空!");
}
//基本参数校验
if(no <= 0){
throw new RuntimeException("学生编号非法!");
}
StudentNode currNode = head.next;//当前节点
StudentNode currBeforeNode = head;//当前节点的前一个节点
while(true){
int currNodeNo = currNode.getNo();
if(no == currNodeNo){
currBeforeNode.next = currNode.next;
return currNode;
}else{
if(null == currNode.next){
break;
}
currBeforeNode = currNode;
currNode = currNode.next;
}
}
System.out.printf("学号 【%s】 不存在!\n",no);
return null;
}
}
class StudentNode{
private int no;//学号
private String name;//学生姓名
private int score;//学生分数
public StudentNode next;//指针
public StudentNode() {
}
public StudentNode(int no, String name, int score) {
this.no = no;
this.name = name;
this.score = score;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "StudentNode{" +
"no=" + no +
", name='" + name + '\'' +
", score=" + score +
'}';
}
}
总结
单向链表简单介绍到这里,如果问题请各位大神积极指教