数据结构----------单链表

数据结构(一)    单链表的实现

一. 单链表的简单介绍

  单链表在整个数据结构中可以说是相对比较简单的,但是它的作用却不可忽视,许多相对复杂的数据结构都是基于单链表实现的。在这里我们简单介绍一下单链表。

  单链表有节点构成,每个节点封装了数据和下一个节点的地址,最后一个节点指向null。

 

二. 单链表的java实现

  (一)API

  该实现单链表可以适合用于存储实现了Comparable接口的任意对象,因此可以对不同节点进行比较,链表内部维护了一个first  和last指针,分别指向链表的头结点和尾节点,size变量存储的是链表的长度。

  内部为了了一个Node类,它的实例用于表示节点对象,存储相应的数据和下一个节点的地址。

  该实现可以用来添加节点、删除节点、遍历节点、反转链表等,具体介绍参见具体实现。

    

  (二)具体实现

  • 添加节点

  1. 插入节点(默认从尾部插入)

/**
     * 向链表中添加节点(从链表尾部添加节点)
     *         如果链表为空,则链表的第一个节点的最后一个节点都指向新添加的节点
     *         如果链表不为空,将最后一个节点的next指向新产生的节点,并将新节点设为最后一个节点
     *         节点数加1
     * @param data
     */
    public void add(T data) {
        Node newNode = new Node(data);
        if(first == null) {//链表为空
            first = newNode;
            last = first;
        }else {//链表不为空
            last.next = newNode;
            last = newNode;
        }
        size++;//节点数加1
    }
    

  2. 从链表头部插入节点 

/**
     * 从链表头部添加元素
     * @param data
     */
    public void addAtHeader(T data) {
        Node newNode = new Node(data);
        if(first == null) {
            first = newNode;
            last = first;
        }else {
            newNode.next = first;
            first = newNode;
        }
        size++;
    }

 

  • 删除节点

  1. 删除头部节点/**     * 删除头结点

     * @return
     */
    public T deleteFirst() {
     if(first == null)
      return null;
T data
= first.data; first = first.next; size--; return data; }

  2. 删除指定位置节点 

/**
     * 删除指定位置的节点
     * @param index
     * @return
     */
    public T deleteNode(int index) {
        Node preCurrent = null;
        Node current = first;
        if(index == 0) {
            return deleteFirst();
        }
        int i = 0;
        while(i < index) {
            preCurrent = current;
            current = current.next;
            i++;
        }
        preCurrent.next = current.next;
        size--;
        return current.data;
    }
    
  • 遍历节点

  1. 遍历节点

/**
     * 遍历打印所有节点
     */
    public void print(Node node) {
        Node current = node;
        int i = 0;
        while(current != null) {
            System.out.println("This is the "+i+"th Node:"+current.data);
            current = current.next;
            i++;
        }
    }

  2. Iterable实现节点遍历

/**
     * 遍历方法2: 链表实现Iterable接口,返回一个Iterator对象
     *         
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            Node current = first;//内部维护一个当前节点对象
            @Override
            public boolean hasNext() {
                return current != null;
            }

            @Override
            public T next() {
                Node returnNode = current;
                current = current.next;//小心
                return returnNode.data;
            }
            
        };
    }
  • 反向遍历节点
/**
     * 
     * 从为到头遍历链表:递归实现
     * 也可采用栈,正向遍历压栈,循环完毕,弹栈输出
     */
    public void printFromLastToFirst() {
        reversePrint(first);
    }
    
    public void reversePrint(Node n) {
        if(n == null) {
            return;
        }
        reversePrint(n.next);
        System.out.println("Node: "+n.data);
    }
  • 链表反转
    /**
         * 链表反转:遍历实现
         * 返回反转后链表的头结点
         */
        public Node reverseNode(Node head) {
            Node pre = null;
            while(head != null) {
                Node temp = head.next;//保留当前头结点的下一个节点
                head.next = pre;
                pre = head;
                head = temp;
            }
            return pre;
        }
    /**
         * 链表反转:遍历实现
         * 思想:翻转head->为首的链表, 然后head变为尾部节点
         */
        public Node reverseNode(Node head) {
            if(head == null || head.next == null) { 
                return head;
            }
            Node pre = reverseNode(head.next);
            head.next.next = head;
            head.next = null;
            return prev;
        }
  • 返回链表第k个元素

  1. 实现1

/**
     * 返回链表倒数第k个节点(实现1)
     *         1. 返回链表总长度,计算倒数第k个节点的索引
     *         2. 遍历并返回倒数第k个节点
     * @param index
     * @return
     */
    public T findLastNode1(int index) {
        Node returnNode = first;
        int k = size()-index;//k为要返回节点的索引
        if(k < 0) {
            throw new NullPointerException("链表长度越界 ");
        }
        for(int i = 0; i < k; i++) {
            returnNode = returnNode.next;
        }
        return returnNode.data;
    }
    

  2. 实现2

/**
     * 返回链表倒数第k个元素(实现2)
     *         倒数第k个元素和最后一个元素相差k-1,所以可以选择两个指针
     *         1. 将第一个指针从头结点移动k-1次,使头结点和这个节点相差k-1
     *         2. 同时移动两个节点,当该节点是最后一个节点时,头结点为倒数第k个节点
     * @param index
     * @return
     */
    public T findLastNode2(int index) {
        Node firstNode = first;
        Node lastNode = first;
        //lastNode向后移动k-1次
        for(int i = 0; i < index - 1; i++) {
            if(lastNode == null) {
                throw new NullPointerException("链表长度越界");
            }
            lastNode = lastNode.next;
        }
        //同时移动,指针lastNode为最后一个节点
        while(lastNode.next != null) {
            firstNode = firstNode.next;
            lastNode  = lastNode.next;
        }
        
        return firstNode.data;
        
    }
  • 合并两个有序链表 
/**
     * 合并两个有序链表
     * @param head2 要合并的链表的头结点
     * @return 合并后的有序链表的头结点
     */
    public Node merge(Node head2) {
        Node head1 = this.first;//当前链表的头结点
        if(head1 == null && head1 == null) 
            return null;
        if(head1 == null) return head2;
        if(head2 == null) return head1;
        
        Node head = null;//新生成链表的头结点
        Node current = null;//新生成链表的当前节点
        while(head1 != null || head2 != null) {//至少一个链表没有循环完
            //1.为新生成的链表添加第一个节点
            if(head == null) {
                if(head1.data.compareTo(head2.data) < 0) {
                    head = head1;
                    current = head;
                    head1 = head1.next;
                }else {
                    head = head2;
                    current = head;
                    head2 = head2.next;
                }
            }
            //2.为生成的节点添加后续节点
            //如果head1为空,直接将当前节点指向head2,返回head即可
            if(head1 == null) {
                current.next = head2;
                return head;
            }
            //如果head2为空,直接将当前节点指向head1,返回即可
            if(head2 == null) {
                current.next = head1;
                return head;
            }
            //如果两个节点都不为空,进行比较,比较后继续循环
            if(head1.data.compareTo(head2.data) < 0) {
                current.next = head1;
                current = head1;
                head1 = head1.next;
            }else {
                current.next = head2;
                current = head2;
                head2 = head2.next;
            }
        }
        return head;
        
    }
    
  • 判断链表是否有环 
    /**
         * 判断单链表是否有环
         *         使用两个指针,first指针每次移动一步,second指针每次移动2步
         * @return
         */
        public boolean hasCycle() {
            Node first = this.first;
            Node second = this.first;
            
            while(second != null) {
                first = first.next;//每次移动一次
                second = second.next.next;//每次移动两次
                if(first == second) {
                    return true;
                }
            }
            return false;
        }

     

 

posted @ 2018-05-30 16:31  sowhat1943  阅读(213)  评论(0编辑  收藏  举报