zoukankan      html  css  js  c++  java
  • 数据结构之单链表(合并两个单链表有序化、单链表反转、头插尾插,CRUD)

    单链表

    单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。

      看图说话:

      上图是单链表在内存中的存储结构,也许我们常常熟悉的单链表是这样的:

       但是不能单纯的以为它是按顺序存储,这里只是为了形象的展示单链表罢了,它的next域指向的不一定是按某种顺序的。

    接下来通过一个小Demo来全面实践单链表

      先来定义结点的属性:假设以《水浒传》中的一位英雄作为一个结点,no代表他的编号,name表示名字,nickname表示昵称,next为堆中指向下一个英雄结点的地址。(请忽略属性权限)

     1 class HeroNode {
     2     public int no;
     3     public String name;
     4     public String nickname;
     5     public HeroNode next;
     6 
     7     public HeroNode(int hNo, String hName, String hNickname) {
     8         this.no = hNo;
     9         this.name = hName;
    10         this.nickname = hNickname;
    11     }
    12 
    13     @Override
    14     public String toString() {
    15         return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
    16     }
    17 }

      有了结点类,就可以创建头结点,进而实现一条单链表了

    private HeroNode head = new HeroNode(0, "", "");

       首先可以以head结点为头结点,来加入结点,添加的方法分为 :1、 按照编号no顺序添加(即升序,但不能存在相同no) 2、尾插法   3、头插法

    1、 按照编号no顺序添加(即升序,但不能存在相同no)

      具体操作由两行代码搞定(也是头插法的代码)

     heroNode.next = temp.next;
    temp.next = heroNode;

       接着添加第二个结点(no小于第一个结点)


     

     具体代码如下:

     1 public void addByOrder(HeroNode heroNode) {//传入一个结点
     2         HeroNode temp = head;//用一个temp变量代替head,方便遍历
     3         boolean flag = false;//标记单链表中是否存在与heroNode节点no相同的结点
     4         while (true) {//遍历单链表进行判断
     5             if (temp.next == null)//如果头节点指向为空,则直接跳出循环
     6                 break;
     7             if (temp.next.no > heroNode.no)//传入的结点no与单链表中的no比较
     8                 break;
     9             if (temp.next.no == heroNode.no) {//有重复,则标记true
    10                 flag = true;
    11                 break;
    12             }
    13             temp = temp.next;//遍历,指向下一个结点
    14         }
    15         if (!flag) {//前两个if跳出循环,都会执行,具体看下图
    16             heroNode.next = temp.next;
    17             temp.next = heroNode;
    18         } else {
    19             System.out.println("存在" + heroNode.no);
    20         }
    21     }

    2、尾插法

     1 public void addEnd(HeroNode heroNode) {
     2         // 尾插法需要遍历单链表到最后一个结点的下一个结点为null的上一个结点
     3         HeroNode temp = head;
     4         while (true) {
     5             if (temp.next == null)
     6                 break;
     7             else
     8                 temp = temp.next;
     9         }
    10         temp.next = heroNode;
    11     }

    3、头插法

    1 public void addFirst(HeroNode heroNode) {
    2         HeroNode temp = head;
    3         heroNode.next = temp.next;
    4         temp.next = heroNode;
    5     }

    进行了单链表的添加后,就要进行简单的更新(修改和删除),这里就比较简单了,注意这里的头结点不可删除

    由于第二个结点在堆内存中没有指向任何结点,而且没有任何节点指向改no为2的结点,于是该对象随后会被GC回收掉

    删除结点代码:

     1 public void delete(HeroNode heroNode) {
     2         HeroNode temp =head;
     3         while (true) {
     4             if (temp.next == null) {
     5                 System.out.println("链表为空");
     6                 break;
     7             }
     8             if (temp.next == heroNode) {
     9                 temp.next = heroNode.next;
    10                 heroNode.next = null;
    11                 break;
    12             }
    13             temp = temp.next;
    14         }
    15     }

    修改结点代码:

     1 public void update(HeroNode heroNode, String name) {
     2         HeroNode temp = head;
     3         while (true) {
     4             if (temp.next == null) {
     5                 System.out.println("链表为空");
     6                 break;
     7             }
     8             if (temp.next == heroNode) {
     9                 heroNode.name = name;
    10                 break;
    11             }
    12             temp = temp.next;
    13         }
    14     }

      ok,到此单链表的增删改查结束了,接下来进行进阶:单链表反转、合并两个单链表并有序

    单链表反转(其实就是头插法,不难)

     借助一个新的头结点来生成反向单链表,最后再由原来的头结点指向这个新的头结点的下一个结点即可,分析图如下:

     temp用来代替头结点进行移动操作,next用于保存temp的下一个结点,reverseNode为辅助头结点

       得到:

      进行头插法

     

     

     具体反转单链表代码:

     1 public void revList() {
     2         // 关键就是借助了下面这个辅助头节点
     3         HeroNode reverseNode = new HeroNode(0, "", "");
     4         // 下面这个是要保存当前节点的下一个节点的
     5         HeroNode next = null;
     6         HeroNode temp = head.next;// 将head后的一大串单链表copy一份交给temp管理
     7         while (true) {
     8             if (temp == null) {
     9                 break;// 单链表为空
    10             }
    11             next = temp.next;// 保存下一个节点,比如当前temp->1,那么next就等于temp->2
    12             temp.next = reverseNode.next;// 1的next赋值为null,下一次就是2的next赋值为1
    13             reverseNode.next = temp;// 这一步就是上面“下一次里的next”赋值为1的操作
    14             temp = next;// 到这一步的时候,本次temp已经和原来的单链表断开了,所以需要next提前保存temp.next
    15         }
    16         head.next = reverseNode.next;
    17     }

    合并两个单链表并有序

      思路:将按照升序加入结点的两个单链表进行升序合并。

     以此规律可得到结果:

     具体升序合并单链表代码:

     1 public SingleLinkedList merge(SingleLinkedList sl, SingleLinkedList sl2) {
     2         SingleLinkedList ss = new SingleLinkedList();
     3         HeroNode temp1 = sl.head.next;
     4         HeroNode temp2 = sl2.head.next;
     5         HeroNode next = null;
     6         while (true) {
     7             if (temp1 == null && temp2 == null) {//当两个链表都遍历完时
     8                 break;
     9             }
    10             if (temp1 == null && temp2 != null) {//当其中一个链表还有结点,但是另一个已经遍历完,则将此链表剩余部分依次加入新链表ss
    11                 next=temp2.next;
    12                 ss.addByOrder(temp2);
    13                 temp2 = next;
    14             } else if (temp2 == null && temp1 != null) {//同上
    15                 next=temp1.next;
    16                 ss.addByOrder(temp1);
    17                 temp1 = next;
    18             }
    19             if (temp1 != null && temp2 != null) {//都没有遍历完
    20                 if (temp1.no < temp2.no) {//把no小的加入新链表
    21                     next = temp1.next;//保存下一节点
    22                     ss.addByOrder(temp1);//这一步会导致temp1指向改变,导致链表丢失
    23                     temp1 = next;//所以需要恢复到temp1得下一个
    24                 } else {
    25                     next = temp2.next;
    26                     ss.addByOrder(temp2);
    27                     temp2 = next;
    28                 }
    29             }
    30         }
    31         return ss;
    32     }

    所有随笔,皆为复习巩固。如有错误,欢迎指正。

  • 相关阅读:
    520了,用32做个简单的小程序
    我要偷偷的学Python,然后惊呆所有人(第十三天)
    Python关于import的实验(10) 利用__init__.py文件将变量和包下的模块作为包的属性
    爬虫实战 如何爬取B站视频评论?
    【爬虫】对新笔趣阁小说进行爬取,保存和下载
    年轻就该多尝试,教你20小时Get一项新技能
    基于visual c++之windows核心编程代码分析(66)实现Windows服务的远程控制
    基于visual c++之windows核心编程代码分析(59)实现网络简单代理编程
    Windows Azure Traffic Manager的新的管理API
    现实世界的Windows Azure:采访Aidmatrix的交付副总裁Michael Ross
  • 原文地址:https://www.cnblogs.com/taichiman/p/12829185.html
Copyright © 2011-2022 走看看