単一リンクリストのデータ構造と関連するアルゴリズムの問題

1単一リンクリストの説明

(1)単一リンクリストの構成

単一リンクリスト:単一リンクリストは、さまざまなノード(ノード)で構成される線形テーブルです。メモリに格納されたデータは連続的ではありません。格納されたデータはメモリに分散されます。各ノードは、それだけが知ることができます。次のノードの保存場所。単独でリンクされたリストは、N個のノード(ノード)で構成され、各ノードは、このノードのデータ(データ)と記録されているのアドレス(ポインタ、参照)、次のノード外部に公開されているヘッドノード(Head)は1つだけです。リンクリストのすべての操作は、そのヘッドノードを介して直接または間接的に実行され、最後のノードポインタフィールドはNULLを指します。

上記の情報から、リンクリストは次の要素で構成されていることがわかります。

  • 構成:データフィールド+ポインタフィールド
  • データフィールドには特定のデータが格納され、ポインタフィールドには次のノードを指すアドレスが格納されます。簡単に言えば、次のノードのアドレスが格納されます。アドレス情報ではなく、次のノードの位置情報がここに格納されることに注意してください。データフィールドの。
  • Javaでは、ノードはオブジェクトであり、オブジェクトにはデータ属性と参照属性が含まれ、参照は次のオブジェクト(ノード)を指していると簡単に理解できます。Javaで参照型の変数を定義するときは、実際にはオブジェクトが配置されているアドレスを定義します。
  • 単一リンクリストのテールノードがNULLを指している

Java参照とポインターの違いは、次のように参照できます。

引用才是真正含义上的地址,指针不是地址是内存地址,两者关注对象不一样,引用关注的是对象本身,地址随对

象的改变而改变,因为在创建对象的时候,既存数据本身,也将该对象的地址保存其来,和普通变量的地址不一

样,普通变量的地址就指的是内存地址,C中的指针关注的对象就是内存地址,他不关心该地址存储的是什么,只

关系该地址本身,如果该地址内存空间中东西搬走了,指针不会自动改变指向,java中对象重建了地址也变了)

具体的な定義は、次の図を参照しています。

次の図に示すように、Javaはオブジェクトの観点から理解されます。

2単一リンクリストの実現

3単一リンクリストの逆転

(1)反復法の実装

最初に回路図を見1->2->3->4->5て、例としてリンクリスト取り上げます。

リンクリストの問題解決のアイデア:

(1)既知の情報ヘッドノード

(2)先行ノードと後続ノードを定義します

(3)補助ノードを構築する

(4)仮想ヘッドノードを構築します(ヘッドノードの処理に関するいくつかの問題を回避できます)

 

package jttl.jxresearch.com.hive.udf.test;

import java.util.HashSet;
import java.util.TreeSet;

public class MyLink {

//   // Node head = null;
//    class Node {
//
//       Node next = null;
//       int data;
//
//       public Node(int data){
//
//           this.data = data;
//
//       }
//   }

    public static void main(String[] args) {


        MyLink list = new MyLink();
        Node node = new Node(1);
        list.insert(1,node);
        list.insert(2,node);
        list.insert(6,node);
        list.insert(3,node);
        list.insert(4,node);
        list.insert(5,node);
        list.insert(6,node);
        list.display(node);
        Node head = list.removeElements(node, 6);
        //LinkNode head = list.removeElementsByStack(list.head, 6);
        //LinkNode head = list.removeRepeat(list.head);
        list.display(head);
    }


    /**
     * 向链表中插入数据
     *
     * @param data
     */

    public void addNode(int data,Node head){

        Node node = new Node(data);
        if (head == null) {
            head = node;
            return;
        }
       //构建辅助节点
        Node cur = head;
        while(cur.next != null){
            cur = cur.next;

        }
        //遍历结束时,此时tmp节点地址指向为null说明是最后一个节点,只需要将tmp.next赋值为添加节点地址即可
        cur.next = node;

    }
    public void insert(int val,Node head){
        if(head==null){
            head = new Node(val);
        }else{
            Node cur = head;
            while(cur.next!=null){
                cur = cur.next;
            }
            cur.next = new Node(val);
        }
    }

    public void display(Node head){
        System.out.print("list:");
        Node cur = head;
        while(cur!=null){
            System.out.print(cur.data+"->");
            cur = cur.next;
        }
        System.out.print("null");
        System.out.println();
    }

    /**
     *
     * @return 返回节点长度
     */
    public int length(Node head){

        int length = 0 ;

        Node tmp = head;

        while(tmp != null){

            length ++;
            tmp = tmp.next;

        }

        return length;

    }

    /**
     * 删除链表中指定值的节点
     *
     * @param head,dataue
     * @return
     */
   public Node removeElements(Node head,int data){


      //如果是头节点需要删除

       while(head != null){

           if(head.data != data){

               break;

           }

           head = head.next;


       }

       //如果不是头节点需要删除

       Node pre = head;
       Node cur = head.next;
       while (cur != null){

           if (cur.data != data){
               pre = cur;
               
           }
           pre.next = cur.next;

           cur = cur.next;
       }

       return head;
   }
   //这道题还有一个变种,就是删除重复的节点,不是将重复的所有节点删除,而是保留第一个节点,后面如果重复,就删除。这里也给出代码:
   //先给出一道其他算法去重的题目理解一下
    /**
     * 把字符串去重,并升序排序
     * @param str
     * @return
     */
    public static String sort2(String str) {

        //把String变成单一字符数组(获取字符数组)
        String[] chars = str.split("");

        //把字符串数组放入TreeSet中,根据set元素不重复的特性去掉重复元素。根据treeSet的有序性排序
        TreeSet<String> treeSet = new TreeSet();
        for (String s : chars) {
            treeSet.add(s);
        }

        //把treeSet拼接成字符串
        str = "";
        for (String s : treeSet) {
            str += s;
        }
        return str;
    }

    public Node removeRepeat(Node head){
        if(head==null){
            return null;
        }
        HashSet<Integer> set = new HashSet<Integer>();
        set.add(head.data);
        //定义前驱节点
        Node pre = head;
        //定义当前节点
        Node cur = head.next;
        while(cur!=null){
            //如果当前节点不为null则判断当前节点的数据是否在集合中,存在就删除,不存在就继续遍历
            if(set.contains(cur.data)){
                //删除当前节点
                pre.next = cur.next;
            }else{
                set.add(cur.data);
                pre = cur;
            }
            cur = cur.next;
        }
        return head;
    }


    /**
     * 单链表的反转
     *
     * @param head,dataue
     * @return
     */

    public Node reverseLink(Node head){

        //定义前驱节点,初始化为NULL。链表反转后头节点会指向NULL所以将前驱节点定义为NULL
        Node pre = null;
        //定义当前节点
        Node cur = head;
       //当当前节点不为NULL的时候,进行遍历
        while(cur != null){
           //定义下一节点。将当前节点的的指向信息保存起来,因为在下一步,当前节点的指向信息被覆盖了
            Node tmp = cur.next;
            //将当前节点的中指向下一个节点的地址修改为前驱节点
            cur.next = pre;
            //在遍历的过程中,游标(指针)移动。每遍历一次,前驱节点向前移动一次,当前节点向前移动一次。
            //也就是将当前节点的地址给前驱节点,当前节点移动到下一个节点的位置,如此循环
            pre = cur;
            cur = tmp;

        }
        //此时pre是头节点
      return pre;
    }

    /**
     * 单链表的反转:反转从位置m到n的链表(1=<m<=n<=链表长度)。虚拟头节点
     *
     * @param head,dataue
     * @return
     *
     * 解析:本题,还有下一个要介绍的困难级别的题目,算是一种类型题,它们都是要对链表中特定区间中的节点进行操作。面对这种题目,有固定的套路可以帮你简化解题思路,套路如下:
     *
     *     给链表添加虚拟头节点 dummy,这样就不需要再单独考虑头节点了,可以省去很多麻烦;
     *     找到需要操作的链表区间,区间起始节点用 start 表示,结束节点用 end 表示;
     *     对区间上的链表进行操作;
     *     将操作后的链表重新接回原链表,这里我们需要另外两个变量,前驱节点 prev 和后继节点 successor。
     */



}

リンクリストは、仮想ヘッドノードを介して問題を処理します

次の図に示すように、リンクリストの仮想ヘッドノードを構築します。

 ダミーヘッドノードをNULLとして構築し、dummyHeadと呼びます

dumpyHeadノードを構築した後、実際には、先行ノードがheadではなくNULLを指すようにします。

public class LinkedList<E> {

    private class Node{
        public E e;
        public Node next;

        public Node(E e, Node next){
            this.e = e;
            this.next = next;
        }

        public Node(E e){
            this(e, null);
        }

        public Node(){
            this(null, null);
        }

        @Override
        public String toString(){
            return e.toString();
        }
    }

}

public class LinkedList<E> {

    private class Node{
        public E e;
        public Node next;

        public Node(E e, Node next){
            this.e = e;
            this.next = next;
        }

        public Node(E e){
            this(e, null);
        }

        public Node(){
            this(null, null);
        }

        @Override
        public String toString(){
            return e.toString();
        }
    }

    private Node head;
    private int size;

    public LinkedList(){
        head = null;
        size = 0;
    }

    // 获取链表中的元素个数
    public int getSize(){
        return size;
    }

    // 返回链表是否为空
    public boolean isEmpty(){
        return size == 0;
    }

    // 在链表头添加新的元素e
    public void addFirst(E e){
//        Node node = new Node(e);
//        node.next = head;
//        head = node;

        head = new Node(e, head);
        size ++;
    }



    // 在链表的index(0-based)位置添加新的元素e
    // 在链表中不是一个常用的操作,练习用:)
    public void add(int index, E e){

        if(index < 0 || index > size)
            throw new IllegalArgumentException("Add failed. Illegal index.");

        if(index == 0)
            addFirst(e);
        else{
            Node prev = head;
            for(int i = 0 ; i < index - 1 ; i ++){

                prev = prev.next;

             }
                

//            Node node = new Node(e);
//            node.next = prev.next;
//            prev.next = node;
           //优雅的写法
            prev.next = new Node(e, prev.next);
            size ++;
        }
    }

    // 在链表的index(0-based)位置添加新的元素e
    // 在链表中不是一个常用的操作,练习用:)
   //优雅的写法,通过虚拟头结点,避免处理头节点的问题
    public void add(int index, E e){

        if(index < 0 || index > size)
            throw new IllegalArgumentException("Add failed. Illegal index.");

        Node dummyHead  = new Node(null,null);

        Node prev = dummyHead;
        for(int i = 0 ; i < index ; i ++)
            prev = prev.next;

        prev.next = new Node(e, prev.next);
        size ++;
    }


    // 在链表末尾添加新的元素e
    public void addLast(E e){
        add(size, e);
    }
}

public class LinkedList<E> {

    private class Node{
        public E e;
        public Node next;

        public Node(E e, Node next){
            this.e = e;
            this.next = next;
        }

        public Node(E e){
            this(e, null);
        }

        public Node(){
            this(null, null);
        }

        @Override
        public String toString(){
            return e.toString();
        }
    }

    private Node dummyHead;
    private int size;

    public LinkedList(){
        dummyHead = new Node();
        size = 0;
    }

    // 获取链表中的元素个数
    public int getSize(){
        return size;
    }

    // 返回链表是否为空
    public boolean isEmpty(){
        return size == 0;
    }

    // 在链表的index(0-based)位置添加新的元素e
    // 在链表中不是一个常用的操作,练习用:)
    public void add(int index, E e){

        if(index < 0 || index > size)
            throw new IllegalArgumentException("Add failed. Illegal index.");

        Node prev = dummyHead;
        for(int i = 0 ; i < index ; i ++)
            prev = prev.next;

        prev.next = new Node(e, prev.next);
        size ++;
    }

    // 在链表头添加新的元素e
    public void addFirst(E e){
        add(0, e);
    }

    // 在链表末尾添加新的元素e
    public void addLast(E e){
        add(size, e);
    }

    // 获得链表的第index(0-based)个位置的元素
    // 在链表中不是一个常用的操作,练习用:)
    public E get(int index){

        if(index < 0 || index >= size)
            throw new IllegalArgumentException("Get failed. Illegal index.");

        Node cur = dummyHead.next;
        for(int i = 0 ; i < index ; i ++)
            cur = cur.next;
        return cur.e;
    }

    // 获得链表的第一个元素
    public E getFirst(){
        return get(0);
    }

    // 获得链表的最后一个元素
    public E getLast(){
        return get(size - 1);
    }

    // 修改链表的第index(0-based)个位置的元素为e
    // 在链表中不是一个常用的操作,练习用:)
    public void set(int index, E e){
        if(index < 0 || index >= size)
            throw new IllegalArgumentException("Set failed. Illegal index.");

        Node cur = dummyHead.next;
        for(int i = 0 ; i < index ; i ++)
            cur = cur.next;
        cur.e = e;
    }

    // 查找链表中是否有元素e
    public boolean contains(E e){
        Node cur = dummyHead.next;
        while(cur != null){
            if(cur.e.equals(e))
                return true;
            cur = cur.next;
        }
        return false;
    }

    // 从链表中删除index(0-based)位置的元素, 返回删除的元素
    // 在链表中不是一个常用的操作,练习用:)
    public E remove(int index){
        if(index < 0 || index >= size)
            throw new IllegalArgumentException("Remove failed. Index is illegal.");

        Node prev = dummyHead;
        for(int i = 0 ; i < index ; i ++)
            prev = prev.next;

        Node retNode = prev.next;
        prev.next = retNode.next;
        retNode.next = null;
        size --;

        return retNode.e;
    }

    // 从链表中删除第一个元素, 返回删除的元素
    public E removeFirst(){
        return remove(0);
    }

    // 从链表中删除最后一个元素, 返回删除的元素
    public E removeLast(){
        return remove(size - 1);
    }

    // 从链表中删除元素e
    public void removeElement(E e){

        Node prev = dummyHead;
        while(prev.next != null){
            if(prev.next.e.equals(e))
                break;
            prev = prev.next;
        }

        if(prev.next != null){
            Node delNode = prev.next;
            prev.next = delNode.next;
            delNode.next = null;
        }
    }

    @Override
    public String toString(){
        StringBuilder res = new StringBuilder();

        Node cur = dummyHead.next;
        while(cur != null){
            res.append(cur + "->");
            cur = cur.next;
        }
        res.append("NULL");

        return res.toString();
    }
}

リートコード演習の詳細な説明

public class ListNode {

    int val;
    ListNode next;

    public ListNode(int val) {
        this.val = val;
    }

    //链表节点的构造函数
    //使用arr为参数,创建一个链表,当前的ListNode为链表的头节点
    public ListNode(int[] arr){

        if(arr.length == 0 || arr == null){

            throw new IllegalArgumentException("arr can not be empty");

        }

        this.val = arr[0];
        ListNode cur = this;
        for (int i = 1; i <arr.length ; i++) {

            cur.next = new ListNode(arr[i]);
            cur=cur.next;

        }

    }
    @Override
    public String toString(){

        StringBuilder res = new StringBuilder();

        ListNode cur = this;
        while(cur != null){
            res.append(cur.val + "->");
            cur=cur.next;
        }
        res.append("NULL");
        return res.toString();

    }
}
public class testLeetcode {
     //虚拟头节点解法
    public ListNode removeElements(ListNode head,int val){

        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = head;
        ListNode prev=dummyHead;
        ListNode cur=prev.next;

        while(cur!=null){

            if(cur.val == val){

                prev.next=cur.next;

            }
            //不能写在else语句块中,否则while条件执行不了,while条件更新的状态一定在while语句块中
            prev = cur;
            cur=cur.next;
        }
        //返回头节点
        return dummyHead.next;


    }

    public ListNode removeElements2(ListNode head,int val){


        //如果是头节点需要删除

        while(head != null){

            if(head.val != val){

                break;

            }

            head = head.next;


        }

        //如果不是头节点需要删除

        ListNode pre = head;
        ListNode cur = head.next;
        while (cur != null){

            if (cur.val == val){
                pre.next = cur.next;

            }
            pre = cur;
            cur = cur.next;
        }

        return head;
    }

    public static void main(String[] args) {
        int[] nums = {1, 2, 3, 4, 6, 5, 6};
        ListNode head = new ListNode(nums);
        System.out.println(head);
        ListNode res = (new testLeetcode()).removeElements(head, 1);
        System.out.println(res);
        
    }
}

 

おすすめ

転載: blog.csdn.net/godlovedaniel/article/details/115121236