数据结构-单链表(一)(Java版)

 

目录

1,什么是单链表?

2,单链表的实现

2.1,单链表的节点类型

2.2,单链表的实现

2.2.1,定义一个单链表

2.2.1,头插法插入一个节点

2.2.2,采用尾插法插入一个节点

2.2.3,删除单链表中的一个节点

2.2.4,打印出一条单链表

3,关于单链表面试题目简单练习

3.1,查找单链表中第k个节点

3.2,单链表的反转

3.3,从尾到头打印单链表

3.4,合并两个有序的单链表

3.5,测试代码

3.6,单链表的小结


1,什么是单链表?

单链表 [LinkedList]:由各个内存结构通过一个 Next 指针链接在一起组成,每一个内存结构都存在后继内存结构【链尾除外】,内存结构由数据域和 Next 指针域组成。简单来说可以想象成下面这张图片:

  • 头指针
    • 在线性表的链式存储结构中,头指针是指链表指向第一个结点的指针,若链表有头结点,则头指针就是指向链表头结点的指针。
    • 头指针具有标识作用,故常用头指针冠以链表的名字。
    • 无论链表是否为空,头指针均不为空。头指针是链表的必要元素。
  • 头结点
    • 头结点是为了操作的统一与方便而设立的,放在第一个元素结点之前,其数据域一般无意义(当然有些情况下也可存放链表的长度、用做监视哨等等)。
    • 有了头结点后,对在第一个元素结点前插入结点和删除第一个结点,其操作与对其它结点的操作统一了。
    • 首元结点也就是第一个元素的结点,它是头结点后边的第一个结点。
    • 头结点不是链表所必需的。

2,单链表的实现

2.1,单链表的节点类型

也就是定义单链表的节点类型,每一个节点包含一个data域和一个next域。

class Node{
   public int data;
   public Node next;//指向下一个节点
//构造器
    public Node(int data) {
        this.data = data;
    }
    @Override
    public String toString() {
        return "HeroNode{" +
                "data=" + data +
                '}';
    }
}

2.2,单链表的实现

2.2.1,定义一个单链表

class SingleLinkedList{
//        先定义一个头结点,不存放具体的数据
    private Node head=new Node(0);

    public Node getHead() {
        return head;
    }
}

2.2.1,头插法插入一个节点

/**
     * 采用头插法插入一个节点
     * @param node 需要插入的节点
     */
    public void addHead(Node node){
        if(this.getHead() == null)
            return;
        //不空就把节点查到头处
        Node node1;
        node1=head.next;
        head.next=node;
        node.next=node1;
        node1=null;
    }

2.2.2,采用尾插法插入一个节点

/**
     * 采用尾插法插入一个节点
     * @param node 需要插入的节点
     */
    public void add(Node node){
        Node node1=head;//辅助节点指向头结点
//        先找到链表的最后节点,然后把新节点添加到最后
        while (node1.next != null)
            node1=node1.next;
//        跳出循环的时候,node指向链表的尾节点
        node1.next=node;
        node1=node1.next;//现在让node1指向链表的尾节点
    }

2.2.3,删除单链表中的一个节点

 /**
     * 根据值在单链表中删除一个节点
     * @param value 需要删除节点的值
     * @return 删除成功就返回删除的值,删除失败就返回-1
     */
    public int deleteNode(int value){
        int temp;
        Node node=head.next;
        Node node1=head;
        if(node == null)
            return -1;
        else {//链表不空
            while (node.data != value){
                if(node.next== null)//判断是否走到链表末尾
                    return -1;
                node=node.next;
                node1=node1.next;
            }
//            退出循环说明链表没有此元素或者找到此元素
            temp=node.data;
            node1.next=node.next;
            return temp;
        }
    }

2.2.4,打印出一条单链表

/**
     * 打印单链表信息
     */
    public void printLinkedList(){
//        这里让node直接指向第一个元素节点,这样就可以直接判断当前节点是否为空,然后直接打印
//        如果让node指向head头结点,那么判断的时候需要用node.next != null,链表末尾元素无法打印
        Node node=head.next;
        if(node!= null){
            while (node!= null)
            {
                System.out.print(node.data+ " ");
                node=node.next;
            }
        }
    }

3,关于单链表面试题目简单练习

3.1,查找单链表中第k个节点

/**
     * 题目二:查找单链表中倒数第k个节点
     * @param head 单链表而定头结点
     * @param k
     * @return 单链表中倒数第k个节点的值
     * 思路:快慢指针的应用
     */
    public static int getKvalue(Node head,int k){
        Node node=head.next;
        Node node1=head;
        int num1=0;
        while (node!= null)
        {
            if(num1 != k-1){
                num1++;
                node=node.next;
            }
            else {
                node=node.next;
                node1=node1.next;
            }
        }
//        当循环退出的时候,node1指向第k个节点
        return node1.data;
    }

3.2,单链表的反转

/**
     * 题目三:单链表的反转
     * @param head 单链表的头结点
     * 思路,采用头插入法重新建立链表
     */
    public static void reverseLinkedlist(Node head){
        Node node=head.next;
        Node temp=new Node(0);
        Node node1=temp;
        if(head.next==null||head.next.next==null)
            return;
        while (node != null){
            node1=temp.next;
           temp.next=node;
            node=node.next;
           node1=temp;
        }
       head=temp;
    }

3.3,从尾到头打印单链表

 /**
     * 题目四:从尾到头打印单链表
     * @param head 单链表的头结点
     */
    public  void printRearToHead(Node head){
        Node node=head;
        if(node== null){
            return;
        }
        printRearToHead(node.next);
        System.out.print(node.data+" ");
    }

3.4,合并两个有序的单链表

 /**
     * 合并两个有序的单链表,合并之后还是有序的
     * @param head1 链表一的头结点
     * @param head2 链表二的头结点
     * @return 返回合并后链表的头结点
     */
    public static  Node mergeLinked(Node head1,Node head2){
        //先判断两个链表是否有一个为空,为空就返回另一个链表头结点
        if(head1.next == null)
            return head1;
        if(head2.next == null)
            return head1;
        //如果两个链表都不是空,就申请两个节点分别指向两个链表
        Node node=head1.next;
//        使得head1作为合并后新链表的头节点
//        指向head1的遍历指针
        Node curNode=head1;
        Node node1=head2.next;
        while ((node!=null)&&(node1 != null)){
            if(node.data < node1.data){
                curNode.next=node;
                curNode=node;
                node=node.next;
            }else {
                curNode.next=node1;
                curNode=node1;
                node1=node1.next;
            }
        }
//        循环退出,说明有一条链表已经遍历完毕
        while (node !=null){
            curNode.next=node;
            curNode=node;
            node=node.next;
        }
        while (node1 !=null){
            curNode.next=node1;
            curNode=node1;
            node1=node1.next;

        }
        return head1;
    }

3.5,测试代码

public class SingleLinkedListdemo {
    public static void main(String[] args) {
        SingleLinkedList singleLinkedList=new SingleLinkedList();
        singleLinkedList.add(new Node(1));
        singleLinkedList.add(new Node(3));
        singleLinkedList.add(new Node(5));
        singleLinkedList.add(new Node(7));
        singleLinkedList.add(new Node(9));
//        singleLinkedList.deleteNode(4);
        System.out.println(singleLinkedList.deleteNode(8));
        singleLinkedList.printLinkedList();
        System.out.println();
        System.out.println(singleLinkedList.searchLinked(4));
        System.out.println(getNodeNum(singleLinkedList.getHead()));
       // System.out.println();
        //System.out.println(getKvalue(singleLinkedList.getHead(),1));
//        单链表的反转
        //reverseLinkedlist(singleLinkedList.getHead());
        //singleLinkedList.printLinkedList();
       // SingleLinkedListdemo singleLinkedListdemo=new SingleLinkedListdemo();
       // singleLinkedListdemo.printRearToHead(singleLinkedList.getHead().next);
//        SingleLinkedList singleLinkedList1=new SingleLinkedList();
//        singleLinkedList1.add(new Node(2));
//        singleLinkedList1.add(new Node(4));
//        singleLinkedList1.add(new Node(6));
//        singleLinkedList1.add(new Node(8));
//        singleLinkedList1.add(new Node(10));
//        Node node=mergeLinked(singleLinkedList.getHead(),singleLinkedList1.getHead());

    }
  • 结果展示:

3.6,单链表的小结

  • 链表的主要优势有两点:
    • 一是插入及删除操作的时间复杂度为O(1)。
    • 二是可以动态改变大小。
  • 缺点:
    • 由于其链式存储的特性,链表不具备良好的空间局部性,也就是说,链表是一种缓存不友好的数据结构
    • 访问链表元素需要遍历操作,不可以随机进行访问。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值