这个寒假有点长呀~~~~之前学数据结构与算法时,用C语言实现,现在开始学Java了,就决定用Java重新学习一下,学以致用呀!
每天一点点,积少成多!-_-
学习视频网址:Java数据结构与算法视频教程

单链表

一、链表的特点

  • 链表是有序的列表,以节点的方式来存储;
  • 每个节点包含data域,next域:指向下一个节点;
  • 链表的各个节点不一定是连续存储

二、实例:水浒英雄排行榜管理

1. 要求

使用带head头的单向链表实现–水浒英雄排行榜管理。

  1. 完成对英雄人物的增删改查操作;
  2. 第一种方法在添加英雄时,直接添加到链表的尾部;
  3. 第二种方法在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示).
2 代码实现
2.1 创建英雄节点HeroNode
//定义HeroNode,每个HeroNode对象就是一个节点
class HeroNode{
    public int no;   //排名
    public String name;
    public String nickname;
    public HeroNode next;
    //构造器
    public HeroNode(int no,String name,String nickname){
        this.no =no;
        this.name = name;
        this.nickname = nickname;
    }
    //为了显示方便,重写toString方法
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }
}
2.2 定义SingleLinkList,管理英雄

这个类里面实现各种操作(方法)

class SingleLinkList{
    //先初始化一个头节点,头节点不要动,不存放具体数据
    private HeroNode head = new HeroNode(0,"","");
    
    //add();   添加(不考虑编号顺序)
    //list();   显示链表(遍历)
	//addByOrder();    按照编号顺序添加
	//update();     修改节点的信息
	//del();        删除节点的信息
	.

}
2.2.1 添加节点到单向链表

【备注:不考虑编号顺序】

/**
*	1.找到当前链表的最后一个节点
*	2.将最后这个节点的next指向新的节点
*
*/
   public void add(HeroNode heroNode){
		
		//因为head节点不能动,因此我们需要找一个辅助遍历节点
        HeroNode temp = head;
        while (true){
            if (temp.next == null){
                break;
            }
            temp = temp.next;
        }
        //退出while循环时,temp指向了链表的最后
        //将这个节点的next指向新的节点
        temp.next = heroNode;
    }
2.2.2 显示链表【遍历】
public void list(){
        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;
        }
    }
2.2.3 按照编号的顺序添加

思路如下:

  1. 找到新添加的节点p的位置,通过辅助变量;
  2. 新的节点p.next = temp.next;
  3. temp.next = p;
public void addByOrder(HeroNode heroNode){

        //注意:temp位于添加位置的前一个节点
        HeroNode temp = head;
        boolean flag = false;  //flag标志添加的编号是否存在,默认为false
        while(true){
            if (temp.next == null){
                break;
            }
            if (temp.next.no > heroNode.no){  //位置找到,就在temp的后面插入
                break;
            }else if (temp.next.no == heroNode.no){  //说明希望添加的heroNode编号已经存在
                flag = true;  //编号已存在
                break;
            }
            temp = temp.next;
        }
        //判断flag的值
        if(flag){  //不能添加,编号已经存在
            System.out.printf("准备插入的英雄的编号%d已经存在了,不能加入\n",heroNode.no);
        }else{
            //插入到链表中,temp后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }
2.2.4 修改节点的信息

【备:根据no编号来修改,即不修改no,修改姓名name或者绰号nickname】

//根据newHeroNode的no来修改
    public void update(HeroNode newHeroNode){
        //判断是否为空
        if (head.next == null){
            System.out.println("链表为空");
            return;
        }
        //根据no编号,找到需要修改的节点
        HeroNode temp = head.next;
        boolean flag = false; //表示是否找到该节点
        while(true){
            if (temp == null){  //已经遍历完链表
                break;
            }
            if (temp.no == newHeroNode.no){
                flag = true;  //找到
                break;
            }
            temp = temp.next;
        }
        //根据flag,判断是否找到要修改的节点
        if (flag){
            temp.name = newHeroNode.name;
            temp.nickname = newHeroNode.nickname;
        }else{
            System.out.printf("没有找到编号为%d的节点,不能修改\n",newHeroNode.no);
        }
    }
2.2.5 删除节点的信息

从单链表中删除一个节点:

  1. 先找到需要删除的这个节点的前一个节点temp;
  2. temp.next = temp.next.next;
  3. 被删除的节点,将不会有其它引用指向,会被垃圾回收机制回收。

【备:比较时,是temp.next.no 和待删除节点的no进行比较】

//删除节点
    public void del(int no){
        HeroNode temp = head;
        boolean flag = false;//标志是否找到待删除节点
        while(true){
            if (temp == null){  //已经遍历完链表
                break;
            }
            if (temp.next.no == no){
                flag = true;  //找到
                break;
            }
            temp = temp.next;
        }
        //判断flag
        if (flag){
            temp.next = temp.next.next;
        }else{
            System.out.printf("待删除的%d节点不存在\n",no);
        }
    }

三、单链表的简单应用

1. 单链表中节点的个数

【备:如果是带头结点的链表,不统计头节点。】

public static int getLength(HeroNode head){
        if (head.next == null){//空链表
            return 0;
        }
        int length = 0;
        HeroNode cur = head.next;
        while(cur != null){
            length++;
            cur = cur.next;
        }
        return length;
    }
2. 单链表的反转

思路:【头插法】

//反转链表
    public static void reverseList(HeroNode head){
        //如果当前链表为空,无需反转
        if ( head.next == null){
            return;
        }
        
        HeroNode cur = head.next;
        HeroNode next = null;//指向当前节点cur的下一个节点
        head.next = null;
        while(cur != null){
            next = cur.next; //暂时保存
            cur.next = head.next;
            head.next = cur;
            cur = next;
        }
    }
3. 合并两个有序的单链表,合并之后的链表依然有序
public static HeroNode MergeOrderedList(HeroNode herohead1,HeroNode herohead2){
        HeroNode cur1 = herohead1.next;
        HeroNode cur2 = herohead2.next;
        
        HeroNode temp1,temp2;

        HeroNode result = new HeroNode(0,"","");
        HeroNode tail = result;  
        result.next = null;
        

        while(cur1 != null && cur2 != null){
            if (cur1.no <= cur2.no){
                temp1 = cur1.next;
                tail.next = cur1;
                cur1 = temp1;
                tail = tail.next;
            }else{
                temp2 = cur2.next;
                tail.next = cur2;
                cur2 = temp2;
                tail = tail.next;
            }
        }

        //其中一个链表不为空
        while (cur1 != null){
            tail.next = cur1;
            tail = tail.next;
            cur1 = cur1.next;
        }
        while (cur2 != null){
            tail.next = cur2;
            tail = tail.next;
            cur2 = cur2.next;
        }
        
        return result;
    }