单向链表.

单项链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域data,另一个是存储下一个结点地址的指针域next。
链表可以是带头节点的,也可以不带头节点,具体看具体的需要。头节点一般默认的data不包含任何数据。

单链表的示意图

物理结构

在这里插入图片描述

逻辑结构

在这里插入图片描述

应用场景

在这里插入图片描述

节点的定义

属性定义:

  1. no(编号)
  2. name(名称)
  3. nickName(外号)
  4. next(下一个节点)
class HeroNode{
    
    
    public int no;
    public String name;
    public String nickName;
    public HeroNode next; // next域

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

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

思路

1:实现第一种添加方法

  1. 首先要先定义一个头节点
  2. 将新的节点添加到链表中最后一个节点的next指向中
  3. 如何找到当前链表中的最后一个节点?
  4. 因为链表中的节点,我们是全部都不能随意变动的,所以我们可以使用一个辅助节点temp,使用temp来不断接受链表的节点遍历,在遍历时依次查看temp的next是否为null,如果为null,则表示这是最后一个节点。

具体的代码实现

 public void add(HeroNode heroNode){
    
    
        // 添加的时候要想办法找到链表的尾节点,让尾节点的next指向heroNode
        // 因为head节点不能动,因此我们需要一个辅助节点temp
        HeroNode temp = head;
        // 遍历链表,找到最后
        while(true){
    
    
            // 找到了最后一个节点
            if(temp.next == null){
    
    
                break;
            }
            // 如果不是最后一个节点
            temp = temp.next;
        }
        temp.next = heroNode;
    }

2: 实现第二种添加方法

  1. 首先,添加需要考虑到No的问题,所以我们在temp接受遍历的节点的是时候,需要考虑到temp和temp.next这两个节点的No是否包含了添加节点的No。有以下三种情况,如下表述。
  2. 遍历到最后,temp.next为空的情况下,temp.No小于节点的No,即节点添加到链表的尾部
  3. 节点的No处于temp.No和temp.next.No之间
  4. temp.No等于节点的No
  5. 同时因为需要考虑到No已经存在的问题,我们定义一个布尔变量flag(是否重复),默认为false,当处于4的情况时,修改flag为true。为了方便在后须修改的时候,可以判断flag的值来判断是否存在重复

代码实现

public void addByNo(HeroNode heroNode){
    
    
        // 辅助节点
        HeroNode temp = head;
        // 表示添加的编号是否存在, 默认为false
        boolean flag = false;
        while(true){
    
    
            // temp已经在链表的最后了
            if(temp.next == null){
    
    
                break;
            }
            // 即节点添加到temp和temp.next之间
            if(temp.next.no > heroNode.no){
    
    
                break;
            }
            else if(temp.next.no == heroNode.no){
    
     //heroNode的编号已经存在了
                flag = true;
                break;
            }
            temp = temp.next;
        }
        // 判断flag的值
        if(flag){
    
    
            // 编号已存在
            System.out.println("待插入的英雄的编号已经存在了,不能加入");
        }else {
    
    
            // 插入
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

3:实现遍历链表

  1. 判断链表是否为空,即head.next==null?
  2. 判断是否已经遍历到最后一个节点,temp.next==?

代码实现

public void list(){
    
    
        // 先判断链表是否为空
        if(head.next == null){
    
    
            System.out.println("链表为空");
            return;
        }
        // 因为头节点不能动,我们需要一个辅助遍历来遍历
        HeroNode temp = head.next;
        //开始遍历
        while(true){
    
    
            // 先判断是否到链表的最后
            if(temp == null){
    
    
                System.out.println("到末尾了");
                break;
            }
            // 输出节点的信息
            System.out.println(temp);
            temp = temp.next;
        }
    }

4:实现修改节点

  1. 定义一个赋值节点temp
  2. 定义一个布尔变量flag来判断是否找到该节点
  3. 我们在遍历链表的时候,只要判断temp.No == 新节点.No?如果相等,则修改flag=true。
  4. 同时还需要判断temp是否到末尾

代码实现

public void update(HeroNode newHeroNode){
    
    
        if (head.next == null){
    
    
            System.out.println("链表为空");
            return;
        }
        HeroNode temp = head.next;
        // 表示是否找到该节点
        boolean flag = false;
        while (true){
    
    
            if (temp == null){
    
    
                break;
            }
            if (temp.no == newHeroNode.no){
    
    
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag){
    
    
            temp.nickName = newHeroNode.nickName;
            temp.name = newHeroNode.name;
        }else {
    
    
            System.out.println("没有匹配的节点信息");
        }
    }

5:节点的删除

  1. temp来接受链表遍历的节点信息
  2. 判断temp.next.no == 节点.no? 如果true,temp.next = temp.next.next。在这里必须判断temp.next.no和节点.no是否相同。因为如是等到temp.no和节点.no相同的时候,无法做到删除的效果。在删除时,是需要相同节点的前一个节点.next --> 相同节点的后一个节点

代码实现

 public void delete(int no){
    
    
        HeroNode temp = head;
        // 是否找到待删除节点
        boolean flag = false;
        while (true){
    
    
            if (temp.next == null){
    
    
                break;
            }
            if (temp.next.no == no){
    
    
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag){
    
    
            temp.next = temp.next.next;
        }else {
    
    
            System.out.println("找不到节点信息");
        }
    }

单链表的面试题

1:求单链表的有效个数,头节点不属于有效节点

  1. 直接遍历链表,并且设置长度,初始值length为0,每遍历一次就length++

代码实现

 public static int getNodesNumber(HeroNode head){
    
    
        // 有效节点的个数
        int length = 0;
        // 判断头节点的下一个节点是否为空
        if (head.next == null){
    
    
            return length;
        }
        // 定义辅助变量
        HeroNode temp = head;
       while (temp.next != null){
    
    
           length++;
           temp = temp.next;
       }
       return length;
    }

2:查找单链表中的倒数第k个节点(新浪面试题)

思路1

  1. 获取单链表的总长度length
  2. 遍历单链表寻找第length-k+1个节点

代码实现

public  HeroNode getNodeByIndex(HeroNode head,int index){
    
    
        // 获取节点的个数
        int length = SingleLinkedList.getNodesNumber(head);
        if(index > length){
    
    
            System.out.println("无有效节点");
            return null;
        }
        HeroNode temp = head;
        int emp = 0;
        while (true){
    
    
            temp = temp.next;
            emp++;
            if (emp == length-index+1){
    
    
                break;
            }
        }
        return temp;
    }

思路2

  1. 设置两个指针front和last
  2. front先走k-1步
  3. front和last一起走
  4. front走到末尾时,返回last

代码实现

public HeroNode getNodeByIndex2(int index){
    
    
        HeroNode front = head;
        HeroNode last = head;

        // front 先走 index-1步
        while (true){
    
    
            // 记录front走的步数
            int size = 0;
            front = front.next;
            size ++;
            if (front == null){
    
    
                return null;
            }
            if(size == index -1){
    
    
                break;
            }
        }
        // front和last一起走
        while (true){
    
    
            front = front.next;
            if(front == null){
    
    
                return last;
            }
            last = last.next;
        }
    }

3:将单链表进行反转(腾讯面试题)

  1. 创建一个新的头节点
  2. 遍历原链表
  3. 将遍历的节点插入到新的头节点前
  4. 将原节点.next = 新头节点.next

代码实现

 public static void reverse(HeroNode head){
    
    
        // 1: 创建一个新的节点的头部
        HeroNode newHead = new HeroNode(0,null,null);
        if (head.next == null || head.next.next ==null){
    
    
            System.out.println("不满足反转的条件");
            return;
        }
        // 定义一个辅助的指针,帮助我们进行遍历
        HeroNode temp = head.next;
        // 指向当前节点的下一个节点
        HeroNode next = null;
        // 开始遍历
        while (temp != null){
    
    
            next = temp.next;
            temp.next = newHead.next;
            newHead.next = temp;
            temp = next;
        }
        head.next = newHead.next;
    }

4:将链表进行逆序打印

方法1:

  1. 先将链表逆序
  2. 将链表打印
public void reverseList(HeroNode head){
    
    
        SingleLinkedList.reverse(head);
        SingleLinkedList.list();
    }

上述的代码调用的方法已在前面的内容已经定义并实现过了的。

方法2:

  1. 使用栈的方式进行输出
 public static void reverseList2(HeroNode head){
    
    
        if (head.next == null){
    
    
            System.out.println("空链表");
            return;
        }
        // 创建一个栈
        Stack<HeroNode> stack = new Stack<>();
        HeroNode temp = head.next;
        while (temp != null){
    
    
            stack.add(temp);
            temp = temp.next;
        }
        // 出栈
        while (stack.size() > 0){
    
    
            System.out.println(stack.pop());
        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_45690465/article/details/120218732