• 链表


    链表

    1. 链表(Linked List)介绍

    链表是有序的列表,但是它在内存中的存储如下图所示:

    1)链表是以节点的方式来存储的,是链式存储

    2)每个节点包含data域,next域:指向下一个节点

    3)参照上图,发现链表的各个节点不一定是连续存储的

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

    单链表(带头节点)逻辑结构示意图如下:

    2. 单链表的应用实列

    使用带 head 头的单向链表实现 –水浒英雄排行榜管理完成对英雄人物的增删改查操作。注意:只介绍增加、删除和修改,查询后面再介绍。

    1)第一种方法:在添加英雄时,直接添加在链表的尾部。

    思路示意图:

    2) 第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)

    思路分析示意图:

    3) 修改节点

    分析思路:

    (1) 先找到该节点,通过遍历,

    (2)   temp.name = newHeroNode.name ;

       temp.nickname= newHeroNode.nickname

    4) 删除节点

    思路分析示意图

    5)代码实现

      1 package com.hut.linkedlist;
      2 
      3 import com.hut.linkedlist.HeroNode;
      4 
      5 /*
      6  * 使用带 head 头的单向链表实现 –水浒英雄排行榜管理完成对英雄人物的增删改查操作。
      7  * */
      8 
      9 public class SingleLinkedListDemo {
     10 
     11     public static void main(String[] args) {
     12         //进行测试
     13         //先创建节点
     14         HeroNode hero1 = new HeroNode(1,"宋江","及时雨");
     15         HeroNode hero2 = new HeroNode(2,"卢俊义","玉麒麟");
     16         HeroNode hero3 = new HeroNode(3,"吴用","智多星");
     17         HeroNode hero4 = new HeroNode(4,"林冲","豹子头");
     18         
     19         //创建要给的链表
     20         SingleLinkedList  singleLinkedList = new SingleLinkedList();
     21         
     22 /*        //加入,不会按照编号大小排列
     23         singleLinkedList.add(hero1);
     24         singleLinkedList.add(hero2);
     25         singleLinkedList.add(hero3);
     26         singleLinkedList.add(hero4);
     27         //显示链表
     28         singleLinkedList.list();
     29 */
     30     
     31         //加入和插入数据节点
     32             //会按照编号的大小排列
     33         singleLinkedList.addByOrder(hero1);
     34         singleLinkedList.addByOrder(hero4);
     35         singleLinkedList.addByOrder(hero3);
     36         singleLinkedList.addByOrder(hero2);
     37         //显示链表
     38         singleLinkedList.list();
     39         
     40         //根据编号删除节点    
     41         System.out.println("=======================");
     42         System.out.println("删除前的链表情况:");
     43         singleLinkedList.list();
     44         
     45         singleLinkedList.del(2);
     46         System.out.println("删除后的链表情况:");
     47         singleLinkedList.list();
     48 
     49     }
     50 
     51 }
     52 
     53 
     54 //定义单链表SingleLinkedList 管理英雄
     55 class SingleLinkedList{
     56     //先初始化一个头节点, 头节点不要动, 不存放具体的数据
     57     private HeroNode head = new HeroNode(0,"","");
     58     
     59     //添加节点到单链表尾部
     60             //思路,当不考虑编号顺序时,
     61             //1、找到当前链表的最后一个节点;2、将最后节点的next指向新的节点
     62     public void add(HeroNode newHeroNode) {
     63         //因为head节点不能动,因此需要一个辅助变量遍历  temp
     64         HeroNode temp = head;
     65         //遍历链表的最后
     66         while(true) {
     67             //找到链表的最后
     68             if(temp.next == null) {
     69                 break;
     70             }
     71             //如果没有找到,temp后移
     72             temp = temp.next;
     73         }
     74         //当while循环结束后,temp就指向了链表的最后
     75             //将最后节点的next指向新的节点
     76         temp.next = newHeroNode;
     77     }
     78     
     79     //添加节点到单链表中
     80             //在添加英雄时,根据编号no将英雄插入到指定的位置
     81             //(如果有这个排名,则添加失败,并给出提示)
     82     public void addByOrder(HeroNode newHeroNode) {
     83         //因为头节点不能动,因此通过一个辅助指针(变量)来帮助找到添加的位置
     84         //因为单链表,因为我们找的 temp 是位于 添加位置的前一个节点,否则插入不了
     85         HeroNode temp = head;
     86         boolean flag = false;  //flag标志添加的编号是否存在,默认为false
     87         while(true) {
     88             if(temp.next == null) {  //说明已经在链表的最后
     89                 break;
     90             }
     91             if(temp.next.no > newHeroNode.no) {  //位置找到,就在temp的后面插入
     92                 break;
     93             }else if(temp.next.no == newHeroNode.no) {  //说明要添加的编号已经存在
     94                 flag = true;
     95                 break;
     96             }
     97             temp = temp.next;
     98         }
     99         //判断flag的值
    100         if(flag) {  //不能添加,编号已经存在
    101             System.out.printf("插入英雄的编号%d已经存在,不能再次添加了。
    ",newHeroNode.no);
    102         }else {
    103             //插入链表中,在temp后面
    104             newHeroNode.next = temp.next;
    105             temp.next=newHeroNode;
    106         }
    107     }
    108     
    109     //修改节点信息
    110             //根据编号no来修改,即no不能改变
    111         public void update(HeroNode newHeroNode) {
    112             //判断是否为空
    113             if(head.next == null) {
    114                 System.out.println("链表为空~~");
    115                 return;
    116             }
    117             //找到需要修改的节点,根据编号no
    118                 //定义一个辅助变量
    119             HeroNode temp = head.next;
    120             boolean flag = false;//表示是否找到该节点
    121             while(true) {
    122                 if(temp == null) {
    123                     break;  //已经遍历完毕
    124                 }
    125                 if(temp.no == newHeroNode.no) {   //找到要修改的节点
    126                     flag = true;
    127                     break;
    128                 }
    129                 temp = temp.next;
    130             }
    131             //根据flag判断是否找到要修改的节点
    132             if(flag) {
    133                 temp.name = newHeroNode.name;
    134                 temp.nickname = newHeroNode.nickname;
    135             }else {
    136                 System.out.printf("没有找到编号为%d的节点,无法修改~~",newHeroNode.no);
    137             }
    138         }
    139         
    140         //删除节点
    141                 //思路:1. head 不能动,因此我们需要一个 temp 辅助节点找到待删除节点的前一个节点
    142                 // 2. 在比较时,是 temp.next.no 和 需要删除的节点的 no 比较
    143             public void del(int no) {
    144                 HeroNode temp = head;
    145                 boolean flag = false;//标志是否找到待删除的节点
    146                 while(true) {
    147                     if(temp.next == null) { //已经到了链表的最后
    148                         break;
    149                     }
    150                     if(temp.next.no == no) {  //找到待删除的前一个节点temp
    151                         flag = true;
    152                         break;
    153                     }
    154                     temp = temp.next;  //temp后移,遍历
    155                 }
    156                 //判断flag是否找到待删除的前一个节点
    157                 if(flag) {
    158                     temp.next = temp.next.next;
    159                 }else {
    160                     System.out.printf(" 要删除的%d节点不存在~~",no);
    161                 }
    162             }
    163     
    164     //显示链表(遍历)
    165     public void list() {
    166         //判断链表是否为空
    167         if(head.next == null) {
    168             System.out.println("链表为空~~");
    169             return;
    170         }
    171         //因为头节点,不能动,因此我们需要一个辅助变量来遍历
    172         HeroNode temp = head.next;
    173         while(true) {
    174             //判断链表是否到最后
    175             if(temp == null) {
    176                 break;
    177             }
    178             //输出节点信息
    179             System.out.println(temp);
    180             //将temp后移,一定要小心
    181             temp = temp.next;
    182         }
    183     }
    184 }
    185 
    186 
    187 //定义HeroNode节点,每个HeroNode对象就是一个节点
    188 class HeroNode{
    189     public int no;   //编号
    190     public String name;    //姓名
    191     public String nickname;    //绰号
    192     public HeroNode next;  //指向下一个节点
    193     
    194     //构造器
    195     public HeroNode(int no,String name,String nickname) {
    196         this.no = no;
    197         this.name = name;
    198         this.nickname = nickname;
    199     }
    200 
    201     //为了显示,重写toString方法
    202     @Override
    203     public String toString() {
    204         return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
    205     }
    206     
    207     
    208 }

    3. 单链表面试题

    单链表的常见面试题有如下:

    1)求单链表中有效节点的个数

    核心代码如下:

     1 //求单链表中有效节点的个数
     2     public static int getLength(HeroNode head) {
     3         if(head.next == null) {  //空链表
     4             return 0;
     5         }
     6         int length = 0;
     7         //定义一个辅助变量,这里我们没有统计头节点
     8         HeroNode cur = head.next;
     9         while(cur != null) {
    10             length ++;
    11             cur = cur.next;   //遍历
    12         }
    13         return length;
    14     }

    2)查找单链表中的倒数第K个节点

    核心代码如下:

     1 //查找单链表中的倒数第K个节点
     2         //思路:
     3         //1、编写一个方法,接收head节点,同时接收一个index
     4         //2、index表示倒数第index节点
     5         //3、先把链表从头到尾遍历,得到链表的总长度getLength
     6         //4、得到size后,我们从链表的第一个节点开始遍历(size - index)个,就可以得到
     7         //5、如果找到了,则返回该节点,否则返回null
     8     public static HeroNode findLastIndexNode(HeroNode head,int index) {
     9         //判断如果链表为空,返回null
    10         if(head.next == null) {
    11             return null;//没有找到
    12         }
    13         //遍历得到的链表长度
    14         int size = getLength(head);
    15         //第二次遍历size - index位置,就是我们要找的第K个节点 
    16         //先做一个index校验
    17         if(index <=0 || index > size) {
    18             return null;
    19         }
    20         //定义一个辅助变量,for循环定位倒数的index位置
    21         HeroNode cur = head.next;
    22         for(int i =0;i< size - index;i ++) {
    23             cur = cur.next;
    24         }
    25         return cur;
    26     }

    3)单链表的反转

    思路分析图解:

     

     核心代码如下:

     1 //链表的反转
     2         //思路:
     3         //1、先定义一个节点reverseHead = new HeroNode();
     4         //2、从头到尾遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最前端
     5         //3、原来链表的head.next = reverseHead.next
     6     public static void reversetList(HeroNode head) {
     7         //如果当前链表为空,或者只有一个节点,则根本不需要反转,直接返回
     8         if(head.next == null || head.next.next == null) {
     9             return;
    10         }
    11         //定义一个辅助的变量,帮助我们遍历原来的链表
    12         HeroNode cur = head.next;
    13         HeroNode next = null;//指向当前节点【cur】的下一个节点
    14         //定义一个新的头节点
    15         HeroNode reverseHead = new HeroNode(0,"","");
    16         //遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表 reverseHead 的最前端
    17         while(cur != null) {
    18             next = cur.next;   //被取出节点的下一个节点
    19             cur.next = reverseHead.next;//将 cur 的下一个节点指向新的链表的最前端
    20             reverseHead.next = cur; //将 cur 连接到新的链表上
    21             cur = next;//让 cur 后移遍历链表
    22         }
    23         //将 head.next 指向 reverseHead.next , 实现单链表的反转
    24         head.next = reverseHead.next;
    25     }

    4)从尾到头打印单链表 【百度,要求方式 1:反向遍历 。 方式 2:Stack 栈】

    思路分析图解:

     核心代码如下:

     1 //单链表的逆序打印代码
     2         //可以利用栈这个数据结构,将各个节点压入到栈中,然后利用栈的先进后出的特点,就实现了逆序打印的效果
     3     public static void reversePrint(HeroNode head) {
     4         //判断链表是否为空
     5         if(head.next == null) {
     6             return;  
     7         }
     8         //创建一个栈,将各个节点压入栈中
     9         Stack<HeroNode> stack = new Stack<HeroNode>();
    10         HeroNode cur = head.next;
    11         //将链表的所有节点压入栈中
    12         while(cur != null) {
    13             stack.push(cur);
    14             cur = cur.next;    //cur后移,压入下一个节点
    15         }
    16         //将栈中的所有节点进行打印,pop出栈
    17         while(stack.size() > 0) {
    18             System.out.println(stack.pop());   //栈的特点时先进后出
    19         }
    20     }
  • 相关阅读:
    WCF的一些基本知识点
    WCF服务编程基础
    Spring IoC 原理猜想(转载)
    利用httponly提升应用程序安全性(转载)
    Ajax:CustomValidator对输入的文本框进行验证
    WCF学习系列
    WCF 契约
    JavaScript:search() 方法
    HDU 1596 find the safest road
    HDU 2063 过山车
  • 原文地址:https://www.cnblogs.com/cleanlife/p/14369997.html
Copyright © 2020-2023  润新知