带头节点链表(java语法实现)

链表是一种非连续,非顺序的存储结构,由一系列的节点组成,节点的链接顺序实现了数据元素的逻辑顺序

相对于数组而言,在增删改操作上大大提高了效率(因为不会因为元素的位置变化引起一系列的变动),但同时又失去了随机查询的优点(因为必须进行遍历查询)

单向带头结点链表的结构如下图,用头结点指向第一个节点,每个结点都有两个值value(结点的值)和next(用于连接下一个结点),利用next将所有节点连接形成链

20181122125846659.png

接下来,进行链表的实现

创建一个节点的类Node,有value(int),next(Node),capacity(int)三个属性,和一个带参构造

class Node {
    int value;
    Node next;
    int capacity;

    public Node(int value) {
        this.value = value;
    }
}

创建链表---createLink(Node head)

创建链表,首先要保证这是一个空链才能继续进行

不断输入数据,每输入一个数据创建一个新节点,然后遍历已有的链表,将新节点追加到链表的末尾

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N6eTIzMzM=,size_16,color_FFFFFF,t_70

    Node newNode = new Node(num);
    //建立一个临时节点,用于遍历
    Node tempNode = head;
    //当该链表没有达到末尾时(即结节点的next值不为空时),继续向后遍历
    while(tempNode.next != null) {
        tempNode = tempNode.next;
    }

    //此时,结点达到最后一个不为空的结点,此时将新节点追加到该链表的末尾,并将容量增加1
    tempNode.next = newNode;

输出链表---prinLink(Node head)

链表不为空则遍历并输出

for(Node node = head.next; node != null; node = node.next) {
            System.out.print(node.value + "\t");
        }

通过节点的位置查找节点的值---findValueByNode(Node head)

从头结点head开始找,记录head所在的位置为0,每遍历一个元素,位置加一,然后看当前位置与要查找的位置是否一致,如果一致则输出该位置的值,否则继续遍历,直至找到

int counting = 0;
        //遍历链表,每遍历一个节点,计数加一,直至遍历到第count个节点
        for(Node tempNode = head.next; tempNode != null; tempNode = tempNode.next) {
            ++counting;
            if(counting == count) {
                System.out.println("the value you find located on " + count + "is: " + tempNode.value);
            }
        }

向链表中添加一个节点,以追加的方式添加---appendNode(Node head)

原理和创建链表是一样的,只不过此时只需要添加一个节点而已,不再赘述

删除元素值为value的节点---(deleteByValue)

关于节点删除的方式,首先需要判断要删除的元素在链表中是否存在,如果存在,我们要记录待删除节点的上一个节点preNode,然后让待删除节点的下一个节点nextNode成为preNode的next节点

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N6eTIzMzM=,size_16,color_FFFFFF,t_70

Node tempNode = head;
        //进行遍历
        while (tempNode.next != null) {
            //如果链表里面的某一个节点值与要删除的值相同,则删除该节点,并将链表大小减一
            if(tempNode.next.value == value) {
                tempNode.next = tempNode.next.next;
                --head.capacity;
                return;
            }
            tempNode = tempNode.next;
        }

        //方法执行到这一步,说明链表中没有要删除的元素值
        System.out.println("there exsit no value you want to delete in link");

向某个指定位置插入节点,value是要插入的节点的值---insertNode(Node head, int value)

要插入的节点为Node,先找到要插入的节点的上一个位置的节点preNode,然后将原来的preNode的next节点记录为nextNode,然后将nextNode赋值给Node的next值,再让Node称为preNode的next节点

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N6eTIzMzM=,size_16,color_FFFFFF,t_70

//用counting记录当前位置,当没有找到正确位置时,链表的节点向后移动,并且计数加一
        int counting = 0;
        Node tempNode = head;
        for(; counting != locate; tempNode = tempNode.next) {
            ++counting;
        }

        //注意,要将原来的节点的下一个节点链到当前节点后面,而当前节点则成为原节点的下一个节点
        Node tmp = tempNode.next;
        newNode.next = tmp;
        tempNode.next = newNode;

对链表中的元素进行排序---sortLink(Node head)

这里采用的方式为简单冒泡排序,有n个元素,则进行n-1次排序,冒泡排序详情见之前的文章https://blog.csdn.net/szy2333/article/details/83118677

其实对于链表的排序和数组的排序原理上是一样的,都是将相邻两个元素进行比较,然后交换位置

先看数组的排序

20181122195101407.png

然后看链表的排序,将链表中的相邻节点的值进行比较,如果前节点的值比后面节点的值大,那么将两个节点的值交换即可

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N6eTIzMzM=,size_16,color_FFFFFF,t_70

for(int i = 1; i < head.capacity; i++) {
            //遍历链表,并相邻两个元素进行比较
            for(Node tempNode = head.next; tempNode.next != null; tempNode = tempNode.next) {
                //如果当前节点的值比下一个节点的值大
                if(tempNode.next.value > tempNode.next.next.value) {
                    //两个节点的值交换
                    tmp = tempNode.value;
                    tempNode.value = tempNode.next.value;
                    tempNode.next.value = tmp;
                }
            } 
        }

前面说的是方式1,还有方式2,但是复杂多了,不多可以用来练练逻辑

方式2的思想是:交换两个节点的位置,但是位置会影响其前后节点位置的变化,所以需要进行分析

要交换两个节点,则需要记录前一个结点preNode,这样能保证有一个不变的量进行变化后的调整

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N6eTIzMzM=,size_16,color_FFFFFF,t_70

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N6eTIzMzM=,size_16,color_FFFFFF,t_70

for(int i = 1; i < head.capacity; i++) {
            //遍历链表,并相邻两个元素进行比较
            for(Node tempNode = head; tempNode.next != null && tempNode.next.next != null; tempNode = tempNode.next) {
                //如果当前节点的值比下一个节点的值大
                if(tempNode.next.value > tempNode.next.next.value) {
                    //两个节点的位置交换
                    tmp2 = tempNode.next.next;
                    tmp1 = tempNode.next;
                    tmp1.next = tempNode.next.next.next;
                    tmp2.next = tmp1;
                    tempNode.next = tmp2;
                }
            } 
        }

对于链表的基本操作大致就是这些,下面是源代码及执行结果

完整代码

package 剑指offer;

import java.util.Scanner;


/*
* 注意的地方:
* 1.先不要急着关闭Scanner流,比如createLink方法中用到了该流,findValueByNode也用到了该流,如果在前者的方法汇总关闭的了流,
*   那么在后者使用这个流时就会出现NoSuchElementException
*   原因:打开Scanner后关闭,也就间接地将System.in也关闭了,因为System.in是个静态流,
*   所以引用其的Scanner对象内都是相同状态的。当操作后关闭流之后这时再调用nextInt,在System.in已经关闭了情况下,
*   不能读取到任何数据,就会产生异常
 * */
public class SingleLinkWithHead {
    public static void main(String[] args) {
        SingleLinkWithHead singleLinkWithHead = new SingleLinkWithHead();
        //创建一个头结点
        Node head = new Node(0);
        createLink(head);
        printLink(head);
        findValueByNode(head);
        addNode(head);
        printLink(head);
        deleteByValue(head, 3);
        printLink(head);
        insertNode(head, 5);
        printLink(head);
        sortLink(head);
        printLink(head);
    }

    //对链表中的元素进行排序,以从小到大的方式,这里面用最简单的冒泡排序
    public static void sortLink(Node head) {
        //如果链表为空或者没有元素,则不进行排序
        if(head.capacity <= 1) {
            System.out.println("it needn't be sorted because there exsit one element at most");
            return;
        }

        Node tmp1 = null;
        Node tmp2 = null;
        //共有capacity个元素,所以需要比较capacity-1次
        for(int i = 1; i < head.capacity; i++) {
            //遍历链表,并相邻两个元素进行比较
            for(Node tempNode = head; tempNode.next != null && tempNode.next.next != null; tempNode = tempNode.next) {
                //如果当前节点的值比下一个节点的值大
                if(tempNode.next.value > tempNode.next.next.value) {
                    //两个链表的位置交换
                    tmp2 = tempNode.next.next;
                    tmp1 = tempNode.next;
                    tmp1.next = tempNode.next.next.next;
                    tmp2.next = tmp1;
                    tempNode.next = tmp2;
                }
            } 
        }
    }


    //向某个指定位置插入节点,value是要插入的节点的值
    // 注意:是在某个位置之后添加,如位置为0,则添加的位置就是head之后的第一个节点
    //      如果位置为3,那么添加的是3之后的第四个节点
    public static void insertNode(Node head, int value) {
        //创建新节点,该节点就是要插入的节点
        Node newNode = new Node(value);
        //输入要插入的节点的位置,如果输入的位置不符合条件,则重新输入
        System.out.println("please input the location be inserted:");
        int locate = 0;
        Scanner sc = new Scanner(System.in);
        while (true) {
            locate = sc.nextInt();
            if(locate >= 0 && locate <= head.capacity) {
                break;
            }
            System.out.println("the location you input is incorrect, please input again:");
        }

        //用counting记录当前位置,当没有找到正确位置时,链表的节点向后移动,并且计数加一
        int counting = 0;
        Node tempNode = head;
        for(; counting != locate; tempNode = tempNode.next) {
            ++counting;
        }

        //注意,要将原来的节点的下一个节点链到当前节点后面,而当前节点则成为原节点的下一个节点
        Node tmp = tempNode.next;
        newNode.next = tmp;
        tempNode.next = newNode;

        //不要忘记插入节点后将链表的长度加一
        ++head.capacity;
    }

    //删除元素值为value的节点,简单起见,之间将要删除的值作为参数传入
    public static void deleteByValue(Node head, int value) {
        //如果链表为空,退出方法
        if(head.next == null) {
            System.out.println("there is no element to delete");
            return;
        }

        //建立临时节点,赋值为head
        Node tempNode = head;
        //进行遍历
        while (tempNode.next != null) {
            //如果链表里面的某一个节点值与要删除的值相同,则删除该节点,并将链表大小减一
            if(tempNode.next.value == value) {
                tempNode.next = tempNode.next.next;
                --head.capacity;
                return;
            }
            tempNode = tempNode.next;
        }

        //方法执行到这一步,说明链表中没有要删除的元素值
        System.out.println("there exsit no value you want to delete in link");
    }

    //向链表中添加一个节点,以追加的方式添加
    public static void addNode(Node head) {
        //先创建一个新节点
        System.out.print("please input the value that will be added:");
        Scanner sc = new Scanner(System.in);
        int value = sc.nextInt();
        Node newNode = new Node(value);

        //建立临时节点,赋值为head
        Node tempNode = head;
        //进行遍历,当表达到末尾时,将新节点追加
        for(; tempNode.next != null; tempNode = tempNode.next) {
        }

        tempNode.next = newNode;
        //添加完节点不要忘记将链表的大小增加1
        ++head.capacity;
    }

    //输入节点的序号,查找该位置的节点的值
    public static void findValueByNode(Node head) {
        //如果链表为空,退出方法
        if(head.next == null) {
            System.out.println("the link is empty or count not in right region");
            return;
        }

        //先给count一个初始值0
        int count = 0;
        System.out.println("please input the count you want to find:");
        Scanner sc = new Scanner(System.in);
        //给count重新输入一个值,如果该值在链表的可查范围内,则结束输入
        while(true) {
            count = sc.nextInt();
            if(count > 0 && count <= head.capacity) {
                break;
            }

            System.out.println("the num you input is not suitable, please input again:");
        }

        //创建一个变量counting进行计数
        int counting = 0;
        //遍历链表,每遍历一个节点,计数加一,直至遍历到第count个节点
        for(Node tempNode = head.next; tempNode != null; tempNode = tempNode.next) {
            ++counting;
            if(counting == count) {
                System.out.println("the value you find located on " + count + "is: " + tempNode.value);
            }
        }
    }

    //打印链表
    public static void printLink(Node head) {
        //链表为空,则无法打印
        if(head.next == null) {
            System.out.println("the link is empty, can't be printed");
            return;
        }

        //不为空,遍历整个链表并打印
        for(Node node = head.next; node != null; node = node.next) {
            System.out.print(node.value + "\t");
        }

        System.out.print("\n");
    }

    //创建链表
    public static void createLink(Node head) {
        //先判断链表是否为空,如果链表不为空,则无需创建,退出该方法
        if(head.next != null) {
            System.out.println("the link is not empty, can't be created");
            return;
        }

        //否则,通过键盘输入要插入的结点的值
        System.out.println("please input the values of link:");
        Scanner sc = new Scanner(System.in);
        while(true) {
            int num = sc.nextInt();
            //当值为0则停止输入
            if(0 == num) {
                break;
            }

            //否则,创建一个新节点,新节点的value就是num
            Node newNode = new Node(num);
            //建立一个临时节点,用于遍历
            Node tempNode = head;
            //当该链表没有达到末尾时(即结节点的next值不为空时),继续向后遍历
            while(tempNode.next != null) {
                tempNode = tempNode.next;
            }

            //此时,结点达到最后一个不为空的结点,此时将新节点追加到该链表的末尾,并将容量增加1
            tempNode.next = newNode;
            ++head.capacity;
        }
        
        //节点录入完毕之后,此时的head就是创建好的链表
    }
}


//结点类,每个结点都有自身的值value,连接的下一个节点next,以及各点连接起来的链的大小capacity
//成员变量会自动赋初值为0或null
class Node {
    int value;
    Node next;
    int capacity;

    public Node(int value) {
        this.value = value;
    }
}

执行结果

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N6eTIzMzM=,size_16,color_FFFFFF,t_70

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值