链表

线性表分为顺序表和链表;

1、顺序存储结构和链式存储结构的区别

  顺序存储结构是在内存中开辟一个连续的空间用来存储数据,因此对于内存的需求和苛求,必须是连续的空间,在数据查找(特别是不按照规律排序的数据),时间复杂度较少,效率高。

  链式存储结构是采取链表指针来指示数据的存储位置,这就可以是在内存中随意的存储,没有必要连续存储空间的要求,对于内存的要求相对比较容易。但是,要是从小到大顺序排列的数据时,链式存储结构的时间复杂度较小,效率高。但是要是不规则排列的数据一般时间复杂度较高,效率较低。

2、单向链表代码:

  

package com.zxc.LinklistLearning;

import org.junit.Test;

/**
 * Created by Administrator on 2018/2/17 0017.
 * 单向链表
 */
public class LinkList {
   public class Node{
       public String data;
       private Node next;

       public Node(String data,Node next){
           this.data=data;
           this.next=next;
       }
   }

   private Node header;
   private Node tail;
   private int size;
   public LinkList(){
       header=null;
       tail=null;
   }


   public void addTail(String data){//尾插法
       if(header==null){
            header=new Node(data,null);
            tail=header;
       }else {
           Node newNode=new Node(data,null);
           tail.next=newNode;
           tail=newNode;
       }
       size++;
   }

   public void addHeader(String data){//头插法
       this.header=new Node(data,header);//创建新节点,让新节点的next指向原来的header,并以新创建的节点作为新header
       //如果插入前是空链表
       if(tail==null){
           tail=header;
       }
       size++;
   }

    /**
     *
     * @param index:要即将插入节点的索引号
     * @return:返回索引指定的现在的节点
     */
   public Node getNodeByIndex(int index){
       Node currentNode=header;//代表从头找
       for(int i=0;i<size;i++){
           if(i==index){
               return currentNode;
           }
           currentNode=currentNode.next;
       }
       return null;
   }

    /**
     *
     * @param data:要插入的数据
     * @param index:索引处插入
     */
   public void insert(String data,int index) {
       if (index < 0 || index > size) {
           System.out.println("线性表索引越界");
       } else {
           if (header == null) {
               this.addTail(data);
           } else {
               if (index == 0) {
                   addHeader(data);
               } else {
                   Node preNode = getNodeByIndex(index-1);
                   preNode.next=new Node(data,preNode.next);//先实例化右面,再赋值左侧
               }
           }
       }
   size++;
   }

   public String delete(int index){
       Node del=null;
       if (index < 0 || index > size) {
           System.out.println("线性表索引越界");
       }
       if(index==0){
           del=header;
           header=header.next;
       }else{//多于一个节点
            Node prev=this.getNodeByIndex(index-1);
            del=prev.next;
            prev.next=del.next;
       }
       del.next=null;
       return del.data;
   }

    @Override
    public String toString() {
      StringBuffer sb=new StringBuffer();
      for(Node current=header;current!=null;current=current.next ){
        sb.append(current.data);
      }
       return sb.toString();
    }

    @Test
    public void ok(){
        this.addHeader("A");
        this.addHeader("B");
        this.addTail("C");

        System.out.println(this);
    }
}

 三、双向链表

  每个节点保留两个引用prev,next,让prev指当前节点的上一个节点,让next指向当前节点的下一个节点,此时链表既可以向前也可以向后访问每个节点,这种形式的链表被称为双向链表。

  由于是双向的,我们查找元素的时候既可以从header处查找,也可以从tail处查找,从哪里查找取决于离哪个节点更近,一般地,可以通过被搜索的index值来判断它更靠近header,还是更靠近tail,如果index<size/2,可以判断位置更靠近header,应从header处搜索,反之应从tail处搜索。

package com.zxc.LinklistLearning;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import org.junit.Test;

/**
 * Created by Administrator on 2018/2/18 0018.
 * 双向链表
 * */
public class DeLinkList {
    private class Node{
        private String data;
        private Node prev;
        private Node next;

        public Node(String data,Node prev,Node next){
            this.data=data;
            this.prev=prev;
            this.next=next;
        }
    }

    private Node header;
    private Node tail;
    private int size;
    public DeLinkList(){
        header=null;
        tail=null;
    }

    public void addHeader(String data){//头插法
        this.header=new Node(data,null,header);//创建新节点,让新节点的next指向原来的header,并以新创建的节点作为新header
        //如果插入前是空链表
        if(tail==null){
            tail=header;
        }
        size++;
    }

    public void addTail(String data){//尾插法
        if(header==null){
            header=new Node(data,null,null);
            tail=header;
        }else {
            Node newNode=new Node(data,tail,null);
            tail.next=newNode;
            tail=newNode;
        }
        size++;
    }

    /**
     *
     * @param index:要即将插入节点的索引号
     * @return:返回索引指定的现在的节点
     */
    public Node getNodeByIndex(int index){
        Node currentNode=header;//代表从头找
        for(int i=0;i<size;i++){
            if(i==index){
                return currentNode;
            }
            currentNode=currentNode.next;
        }
        return null;
    }

    /**
     *
     * @param data:要插入的数据
     * @param index:索引处插入
     */
    public void insert(String data,int index) {
        if (index < 0 || index > size) {
            System.out.println("线性表索引越界");
        } else {
            if (header == null) {
                this.addHeader(data);
            } else {
                if (index == 0) {
                    addHeader(data);
                } else {
                    Node preNode = getNodeByIndex(index-1);
                    Node current=new Node(data,preNode,preNode.next);
                    current.next.prev=current;
                    preNode.next=current;

                }
            }
        }
        size++;
    }

    public String delete(int index){
        Node del=null;
        if (index < 0 || index > size) {
            System.out.println("线性表索引越界");
        }
        if(index==0){
            del=header;
            header=header.next;
        }else{//多于一个节点
            Node prev=this.getNodeByIndex(index-1);
            del=prev.next;
            prev.next=del.next;
            del.next.prev=prev;
        }
        del.next=null;
        return del.data;
    }

    @Override
    public String toString() {
        StringBuffer sb=new StringBuffer();
        for(Node current=header;current!=null;current=current.next){
            sb.append(current.data);
        }
        return sb.toString();
    }

    @Test
    public void ok(){
        addHeader("A");
        addTail("B");
        addHeader("C");
        insert("D",1);
        this.delete(2);
        System.out.println(this);
    }
}

 四、链表高级使用技巧:打印两个有序链表的公共部分

  题目:给定两个有序链表的头指针head1,head2,打印两个链表的公共部分

  分析过程:1、如果head1的值小于head2,则head1往下移动。

       2、如果head2的值小于head1,则head2往下移动。

       3、如果head1的值与head2相等,则打印这个值,然后head1和head2都往下移动。

       4、head1或和head2有任何一个移动到null,这个过程停止。

public class PrintCommonPart {

    public static class Node {
        public int value;
        public Node next;
        public Node(int data) {
            this.value = data;
        }
    }

    public static void printCommonPart(Node head1, Node head2) {
        System.out.print("Common Part: ");
        while (head1 != null && head2 != null) {
            if (head1.value < head2.value) {
                head1 = head1.next;
            } else if (head1.value > head2.value) {
                head2 = head2.next;
            } else {
                System.out.print(head1.value + " ");
                head1 = head1.next;
                head2 = head2.next;
            }
        }
        System.out.println();
    }

    public static void printLinkedList(Node node) {
        System.out.print("Linked List: ");
        while (node != null) {
            System.out.print(node.value + " ");
            node = node.next;
        }
        System.out.println();
    }

    public static void main(String[] args) {
        Node node1 = new Node(2);
        node1.next = new Node(3);
        node1.next.next = new Node(5);
        node1.next.next.next = new Node(6);

        Node node2 = new Node(1);
        node2.next = new Node(2);
        node2.next.next = new Node(5);
        node2.next.next.next = new Node(7);
        node2.next.next.next.next = new Node(8);

        printLinkedList(node1);
        printLinkedList(node2);
        printCommonPart(node1, node2);

    }

}

五、约瑟夫环

  

package com.zxc.LinklistLearning;
public class Jhonsef {
    //定义结点,
    class Node{
        int data;
        Node next;
        Node(int data){
            this.data = data;
        }
    }
    public static void main(String[] args){
        new Jhonsef().yuekill();
    }
    public void yuekill(){
        int n = 3;//定义总人数n
        int m = 3;//和出圈数字m
        //初始化循环列表,头结点first和尾结点last
        Node first = new Node(0);
        first.next = first;  //一个节点的时候,头就是尾,尾就是头
        Node last = first;


        for(int i=1; i<n; i++){//形成单链表   0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
            Node temp = new Node(i);
            last.next = temp;
            last = last.next;
        }

        last.next = first;//0和16连接,最终让其首尾相连,形成循环链表 ,尾接头形成循环链表(last为尾结点)
        //以上就是约瑟夫环

        //执行出圈操作
        System.out.println("出圈顺序为:");
        while(last != last.next){
            //下面for循环后,last是第m个结点的前一个结点
            for(int i=1; i<m; i++){  //遍历要删除节点 前一个出圈数字
                last = last.next;
            }
            //出循环之后,就是要删除的节点,删除第m个结点
            System.out.print(last.next.data+" "); //出圈的数字
            last.next = last.next.next;  //指向要删除的节点的下一节点
        }
        System.out.print("
幸运者是:"+last.data);//原来是10号
    }

}

六、链表和数组的区别

  数组:定义时长度固定(在内存中也是连续的),有下标,不能任意删除某个元素

  链表:长度不固定(可以根据需要申请内存),没有下标,可以根据需要删除。C语言中的链表是通过指针把相邻的节点联系起来,也就是前一个节点通过指针来保存下一个节点的地址,所以如果能得到头结点就能得到下一个节点,一直到最后一个节点。而Java中的链表,由于没有指针,所以它不能使用指针来保存下一个节点的地址,但是我们可以保存下一个节点对象,这种方法叫做引用。

原文地址:https://www.cnblogs.com/television/p/8452186.html