算法与数据结构之线性结构的相关知识,简单易懂。

    xiaoxiao2021-03-25  108

    一、    数组、单链表和双链表介绍以及双向链表的Java实现

    1)  概要

    线性表是一种线性结构,它是具有相同类型的n(n≥0)个数据元素组成的有限序列。本章先介绍线性表的几个基本组成部分:数组、单向链表、双向链表;随后给出双向链表的Java语言实现。内容包括:

    数组

    单向链表

    双向链表

    2)  数组

    数组有上界和下界,数组的元素在上下界内是连续的。

    存储10,20,30,40,50的数组的示意图如下:

    数组的特点是:数据是连续的;随机访问速度快。

    数组中稍微复杂一点的是多维数组和动态数组。对于C语言而言,多维数组本质上也是通过一维数组实现的。至于动态数组,是指数组的容量能动态增长的数组;对于C语言而言,若要提供动态数组,需要手动实现;而对于C++而言,STL提供了Vector;对于Java而言,Collection集合中提供了ArrayList和Vector。

    3)  单向链表

    单向链表(单链表)是链表的一种,它由节点组成,每个节点都包含下一个节点的指针。

    单链表的示意图如下:

    表头为空,表头的后继节点是"节点10"(数据为10的节点),"节点10"的后继节点是"节点20"(数据为10的节点),...

    单链表删除节点

    删除"节点30"

    删除之前:"节点20"的后继节点为"节点30",而"节点30" 的后继节点为"节点40"。

    删除之后:"节点20"的后继节点为"节点40"。

    单链表添加节点

    在"节点10"与"节点20"之间添加"节点15"

    添加之前:"节点10"的后继节点为"节点20"。

    添加之后:"节点10"的后继节点为"节点15",而"节点15" 的后继节点为"节点20"。

    单链表的特点是:节点的链接方向是单向的;相对于数组来说,单链表的的随机访问速度较慢,但是单链表删除/添加数据的效率很高。

    4)  双向链表

    双向链表(双链表)是链表的一种。和单链表一样,双链表也是由节点组成,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。

    双链表的示意图如下:

    表头为空,表头的后继节点为"节点10"(数据为10的节点);"节点10"的后继节点是"节点20"(数据为10的节点),"节点20"的前继节点是"节点10";"节点20"的后继节点是"节点30","节点30"的前继节点是"节点20";...;末尾节点的后继节点是表头。

    双链表删除节点

    删除"节点30"

    删除之前:"节点20"的后继节点为"节点30","节点30" 的前继节点为"节点20"。"节点30"的后继节点为"节点40","节点40" 的前继节点为"节点30"。

    删除之后:"节点20"的后继节点为"节点40","节点40" 的前继节点为"节点20"。

    双链表添加节点

    在"节点10"与"节点20"之间添加"节点15"

    添加之前:"节点10"的后继节点为"节点20","节点20" 的前继节点为"节点10"。

    添加之后:"节点10"的后继节点为"节点15","节点15" 的前继节点为"节点10"。"节点15"的后继节点为"节点20","节点20" 的前继节点为"节点15"。

    5)  Java实现双链表

    实现代码

    双链表类(DoubleLink.java)

    /**

     *Java 实现的双向链表。

     * 注:java自带的集合包中有实现双向链表,路径是:java.util.LinkedList

     *

     *@author skywang

     *@date 2013/11/07

     */

    public class DoubleLink<T> {

       // 表头

       private DNode<T> mHead;

       // 节点个数

       private int mCount;

       // 双向链表“节点”对应的结构体

       private class DNode<T> {

           public DNode prev;

           public DNode next;

           public T value;

           public DNode(T value, DNode prev, DNode next) {

               this.value = value;

               this.prev = prev;

               this.next = next;

           }

        }

       // 构造函数

       public DoubleLink() {

           // 创建“表头”。注意:表头没有存储数据!

           mHead = new DNode<T>(null, null, null);

           mHead.prev = mHead.next = mHead;

           // 初始化“节点个数”为0

           mCount = 0;

        }

       // 返回节点数目

       public int size() {

           return mCount;

        }

       // 返回链表是否为空

       public boolean isEmpty() {

           return mCount==0;

        }

       // 获取第index位置的节点

       private DNode<T> getNode(int index) {

           if (index<0 || index>=mCount)

               throw new IndexOutOfBoundsException();

           // 正向查找

           if (index <= mCount/2) {

               DNode<T> node = mHead.next;

               for (int i=0; i<index; i++)

                    node = node.next;

     

               return node;

           }

           // 反向查找

           DNode<T> rnode = mHead.prev;

           int rindex = mCount - index -1;

           for (int j=0; j<rindex; j++)

               rnode = rnode.prev;

           return rnode;

        }

       // 获取第index位置的节点的值

       public T get(int index) {

           return getNode(index).value;

        }

       // 获取第1个节点的值

       public T getFirst() {

           return getNode(0).value;

        }

       // 获取最后一个节点的值

       public T getLast() {

           return getNode(mCount-1).value;

        }

       // 将节点插入到第index位置之前

       public void insert(int index, T t) {

           if (index==0) {

               DNode<T> node = new DNode<T>(t, mHead, mHead.next);

               mHead.next.prev = node;

               mHead.next = node;

               mCount++;

               return ;

           }

           DNode<T> inode = getNode(index);

           DNode<T> tnode = new DNode<T>(t, inode.prev, inode);

           inode.prev.next = tnode;

           inode.next = tnode;

           mCount++;

           return ;

        }

       // 将节点插入第一个节点处。

       public void insertFirst(T t) {

           insert(0, t);

        }

       // 将节点追加到链表的末尾

       public void appendLast(T t) {

           DNode<T> node = new DNode<T>(t, mHead.prev, mHead);

           mHead.prev.next = node;

           mHead.prev = node;

           mCount++;

        }

       // 删除index位置的节点

       public void del(int index) {

           DNode<T> inode = getNode(index);

           inode.prev.next = inode.next;

           inode.next.prev = inode.prev;

           inode = null;

           mCount--;

        }

       // 删除第一个节点

       public void deleteFirst() {

           del(0);

        }

       // 删除最后一个节点

       public void deleteLast() {

           del(mCount-1);

        }

    }

    测试程序(DlinkTest.java)

    /**

     *Java 实现的双向链表。

     * 注:java自带的集合包中有实现双向链表,路径是:java.util.LinkedList

     *

     *@author skywang

     *@date 2013/11/07

     */

     

    public class DlinkTest {

       // 双向链表操作int数据

       private static void int_test() {

           int[] iarr = {10, 20, 30, 40};

           System.out.println("\n----int_test----");

           // 创建双向链表

           DoubleLink<Integer> dlink = new DoubleLink<Integer>();

           dlink.insert(0, 20);    // 将 20 插入到第一个位置

           dlink.appendLast(10);    // 将 10 追加到链表末尾

           dlink.insertFirst(30);    // 将 30 插入到第一个位置

           // 双向链表是否为空

           System.out.printf("isEmpty()=%b\n", dlink.isEmpty());

           // 双向链表的大小

           System.out.printf("size()=%d\n", dlink.size());

           // 打印出全部的节点

           for (int i=0; i<dlink.size(); i++)

               System.out.println("dlink("+i+")="+ dlink.get(i));

        }

       private static void string_test() {

           String[] sarr = {"ten", "twenty","thirty", "forty"};

           System.out.println("\n----string_test----");

           // 创建双向链表

           DoubleLink<String> dlink = new DoubleLink<String>();

           dlink.insert(0, sarr[1]);    // 将 sarr中第2个元素 插入到第一个位置

           dlink.appendLast(sarr[0]);    // 将 sarr中第1个元素 追加到链表末尾

           dlink.insertFirst(sarr[2]);    // 将 sarr中第3个元素 插入到第一个位置

           // 双向链表是否为空

           System.out.printf("isEmpty()=%b\n", dlink.isEmpty());

           // 双向链表的大小

           System.out.printf("size()=%d\n", dlink.size());

           // 打印出全部的节点

           for (int i=0; i<dlink.size(); i++)

               System.out.println("dlink("+i+")="+ dlink.get(i));

        }

       // 内部类

       private static class Student {

           private int id;

           private String name;

           public Student(int id, String name) {

               this.id = id;

               this.name = name;

           }

           @Override

           public String toString() {

               return "["+id+", "+name+"]";

           }

        }

       private static Student[] students = new Student[]{

           new Student(10, "sky"),

           new Student(20, "jody"),

           new Student(30, "vic"),

           new Student(40, "dan"),

       };

       private static void object_test() {

           System.out.println("\n----object_test----");

           // 创建双向链表

           DoubleLink<Student> dlink = new DoubleLink<Student>();

           dlink.insert(0, students[1]);   // 将 students中第2个元素插入到第一个位置

           dlink.appendLast(students[0]);   // 将 students中第1个元素追加到链表末尾

           dlink.insertFirst(students[2]);   // 将 students中第3个元素插入到第一个位置

           // 双向链表是否为空

           System.out.printf("isEmpty()=%b\n", dlink.isEmpty());

           // 双向链表的大小

           System.out.printf("size()=%d\n", dlink.size());

           // 打印出全部的节点

           for (int i=0; i<dlink.size(); i++) {

               System.out.println("dlink("+i+")="+ dlink.get(i));

           }

        }

       public static void main(String[] args) {

           int_test();        // 演示向双向链表操作“int数据”。

           string_test();    // 演示向双向链表操作“字符串数据”。

           object_test();    // 演示向双向链表操作“对象”。

        }

    }

    运行结果

    ----int_test----

    isEmpty()=false

    size()=3

    dlink(0)=30

    dlink(1)=20

    dlink(2)=10

    ----string_test----

    isEmpty()=false

    size()=3

    dlink(0)=thirty

    dlink(1)=twenty

    dlink(2)=ten

    ----object_test----

    isEmpty()=false

    size()=3

    dlink(0)=[30, vic]

    dlink(1)=[20, jody]

    dlink(2)=[10, sky]

    二、    栈的图文解析和Java语言的实现

    1)  概要

    本章会先对栈的原理进行介绍,然后通过Java三种语言来演示栈的实现示例。注意:本文所说的栈是数据结构中的栈,而不是内存模型中栈。内容包括:

    1. 栈的介绍

    2. 栈的Java实现

    2)  栈的介绍

    栈(stack),是一种线性存储结构,它有以下几个特点:

    (01) 栈中数据是按照"后进先出(LIFO, Last In First Out)"方式进出栈的。

    (02) 向栈中添加/删除数据时,只能从栈顶进行操作。

    栈通常包括的三种操作:push、peek、pop。

    push -- 向栈中添加元素。

    peek -- 返回栈顶元素。

    pop -- 返回并删除栈顶元素的操作。

    1. 栈的示意图

    栈中的数据依次是 30 --> 20 --> 10

    2. 出栈

    出栈前:栈顶元素是30。此时,栈中的元素依次是 30 --> 20 --> 10

    出栈后:30出栈之后,栈顶元素变成20。此时,栈中的元素依次是 20 --> 10

    3. 入栈

    入栈前:栈顶元素是20。此时,栈中的元素依次是 20 --> 10

    入栈后:40入栈之后,栈顶元素变成40。此时,栈中的元素依次是 40 --> 20 --> 10

    3)  栈的Java实现

    JDK包中也提供了"栈"的实现,它就是集合框架中的Stack类。关于Stack类的原理,在"Java 集合系列07之 Stack详细介绍(源码解析)和使用示例"中,已经详细介绍过了。本部分给出2种Java实现

    Java实现一:数组实现的栈,能存储任意类型的数据。

    Java实现二:Java的 Collection集合中自带的"栈"(stack)的示例。

    1. Java实现一:数组实现的栈,能存储任意类型的数据

    实现代码(GeneralArrayStack.java)

    /**

     *Java : 数组实现的栈,能存储任意类型的数据

     *

     *@author skywang

     *@date 2013/11/07

     */

    import java.lang.reflect.Array;

    public class GeneralArrayStack<T> {

       private static final int DEFAULT_SIZE = 12;

       private T[] mArray;

       private int count;

       public GeneralArrayStack(Class<T> type) {

           this(type, DEFAULT_SIZE);

        }

       public GeneralArrayStack(Class<T> type, int size) {

           // 不能直接使用mArray = new T[DEFAULT_SIZE];

           mArray = (T[]) Array.newInstance(type, size);

           count = 0;

        }

       // 将val添加到栈中

       public void push(T val) {

           mArray[count++] = val;

        }

       // 返回“栈顶元素值”

       public T peek() {

            return mArray[count-1];

        }

       // 返回“栈顶元素值”,并删除“栈顶元素”

       public T pop() {

           T ret = mArray[count-1];

           count--;

           return ret;

        }

       // 返回“栈”的大小

       public int size() {

           return count;

        }

       // 返回“栈”是否为空

       public boolean isEmpty() {

           return size()==0;

        }

       // 打印“栈”

       public void PrintArrayStack() {

           if (isEmpty()) {

               System.out.printf("stack is Empty\n");

           }

           System.out.printf("stack size()=%d\n", size());

            int i=size()-1;

           while (i>=0) {

               System.out.println(mArray[i]);

               i--;

           }

        }

       public static void main(String[] args) {

           String tmp;

           GeneralArrayStack<String> astack = new GeneralArrayStack<String>(String.class);

           // 将10, 20, 30 依次推入栈中

           astack.push("10");

           astack.push("20");

           astack.push("30");

           // 将“栈顶元素”赋值给tmp,并删除“栈顶元素”

           tmp = astack.pop();

           System.out.println("tmp="+tmp);

           // 只将“栈顶”赋值给tmp,不删除该元素.

           tmp = astack.peek();

           System.out.println("tmp="+tmp);

           astack.push("40");

           astack.PrintArrayStack();    // 打印栈

        }

    }

    运行结果:

    tmp=30

    tmp=20

    stack size()=3

    40

    20

    10

    结果说明:GeneralArrayStack是通过数组实现的栈,而且GeneralArrayStack中使用到了泛型。

    2. Java实现二:Java的 Collection集合 中自带的"栈"(stack)的示例

    实现代码(StackTest.java)

    import java.util.Stack;

    /**

     *Java : java集合包中的Stack的演示程序

     *

     *@author skywang

     *@date 2013/11/07

     */

    public class StackTest {

       public static void main(String[] args) {

           int tmp=0;

           Stack<Integer> astack = new Stack<Integer>();

           // 将10, 20, 30 依次推入栈中

           astack.push(10);

           astack.push(20);

           astack.push(30);

           // 将“栈顶元素”赋值给tmp,并删除“栈顶元素”

           tmp = astack.pop();

           //System.out.printf("tmp=%d\n", tmp);

           // 只将“栈顶”赋值给tmp,不删除该元素.

           tmp = (int)astack.peek();

           //System.out.printf("tmp=%d\n", tmp);

           astack.push(40);

           while(!astack.empty()) {

               tmp = (int)astack.pop();

               System.out.printf("tmp=%d\n", tmp);

           }

        }

    }

    运行结果:

    tmp=40

    tmp=20

    tmp=10

    三、    队列的图文解析和对应Java语言实现

    1)  概要

    本章和介绍"栈"时的流程一样,先对队列进行介绍,然后给出队列Java语言的实现。内容包括:

    1. 队列的介绍

    2. 队列的Java实现

    2)  队列的介绍

    队列(Queue),是一种线性存储结构。它有以下几个特点:

    (01) 队列中数据是按照"先进先出(FIFO, First-In-First-Out)"方式进出队列的。

    (02) 队列只允许在"队首"进行删除操作,而在"队尾"进行插入操作。

    队列通常包括的两种操作:入队列 和 出队列。

    1. 队列的示意图

    队列中有10,20,30共3个数据。

    2. 出队列

    出队列前:队首是10,队尾是30。

    出队列后:出队列(队首)之后。队首是20,队尾是30。

    3. 入队列

    入队列前:队首是20,队尾是30。

    入队列后:40入队列(队尾)之后。队首是20,队尾是40。

    3)  队列的Java实现

    JDK包Queue中的也提供了"队列"的实现。JDK中的Queue接口就是"队列",它的实现类也都是队列,用的最多的是LinkedList。本部分介绍给出2种Java实现

    1. Java实现一:数组实现的队列,能存储任意类型的数据。

    2. Java实现二:Java的 Collection集合 中自带的"队列"(LinkedList)的示例。

    1. Java实现一:数组实现的队列,能存储任意类型的数据

    实现代码(ArrayQueue.java)

    /**

     *Java : 数组实现“队列”,只能存储int数据。

     *

     *@author skywang

     *@date 2013/11/07

     */

    public class ArrayQueue {

       private int[] mArray;

       private int mCount;

       public ArrayQueue(int sz) {

           mArray = new int[sz];

           mCount = 0;

        }

       // 将val添加到队列的末尾

       public void add(int val) {

           mArray[mCount++] = val;

        }

       // 返回“队列开头元素”

       public int front() {

           return mArray[0];

        }

       // 返回“队首元素值”,并删除“栈顶元素”

       public int pop() {

           int ret = mArray[0];

           mCount--;

           for (int i=1; i<=mCount; i++)

               mArray[i-1] = mArray[i];

           return ret;

        }

       // 返回“队列”的大小

       public int size() {

           return mCount;

        }

       // 返回“队列”是否为空

       public boolean isEmpty() {

           return size()==0;

        }

       public static void main(String[] args) {

           int tmp=0;

           ArrayQueue astack = new ArrayQueue(12);

           // 将10, 20, 30 依次推入队中

           astack.add(10);

           astack.add(20);

           astack.add(30);

           // 将“队首元素”赋值给tmp,并删除“队首元素”

           tmp = astack.pop();

           System.out.printf("tmp=%d\n", tmp);

           // 只将“队首”赋值给tmp,不删除该元素.

           tmp = astack.front();

           System.out.printf("tmp=%d\n", tmp);

           astack.add(40);

           System.out.printf("isEmpty()=%b\n", astack.isEmpty());

           System.out.printf("size()=%d\n", astack.size());

           while (!astack.isEmpty()) {

               System.out.printf("size()=%d\n", astack.pop());

           }

        }

    }

    运行结果:

    tmp=10

    tmp=20

    isEmpty()=false

    size()=3

    size()=20

    size()=30

    size()=40

    结果说明:ArrayQueue是通过数组实现的队列,而且ArrayQueue中使用到了int型,因此它只支持int类型的数据。

    2. Java实现二:Java的 Collection集合 中自带的"队列"(LinkedList)的示例

    实现代码(QueueTest.java)

    import java.util.Stack;

    /**

     * 用“栈”实现队列

     *

     *@author skywang

     */

    public class StackList<T> {

       // 向队列添加数据时:(01) 将“已有的全部数据”都移到mIn中。 (02) 将“新添加的数据”添加到mIn中。

       private Stack<T> mIn  =null;

       // 从队列获取元素时:(01) 将“已有的全部数据”都移到mOut中。(02) 返回并删除mOut栈顶元素。

       private Stack<T> mOut = null;

       // 统计计数

       private int mCount = 0;

       public StackList() {

           mIn = new Stack<T>();

           mOut = new Stack<T>();

           mCount = 0;

        }

       private void add(T t) {

           // 将“已有的全部数据”都移到mIn中

           while (!mOut.empty())

               mIn.push(mOut.pop());

           // 将“新添加的数据”添加到mIn中

           mIn.push(t);

           // 统计数+1

            mCount++;

        }

       private T get() {

           // 将“已有的全部数据”都移到mOut中

           while (!mIn.empty())

               mOut.push(mIn.pop());

           // 统计数-1

           mCount--;

           // 返回并删除mOut栈顶元素

           return mOut.pop();

        }

       private int size() {

           return mCount;

        }

       private boolean isEmpty() {

           return mCount==0;

        }

       public static void main(String[] args) {

           StackList slist = new StackList();

           // 将10, 20, 30 依次推入栈中

           slist.add(10);

           slist.add(20);

           slist.add(30);

           System.out.printf("isEmpty()=%b\n", slist.isEmpty());

           System.out.printf("size()=%d\n", slist.size());

           while(!slist.isEmpty()) {

               System.out.printf("%d\n", slist.get());

           }

        }

    }

    运行结果:

    tmp=10

    tmp=20

    isEmpty()=false

    size()=3

    tmp=20

    tmp=30

    tmp=40

    仅供个人学习,如有抄袭请包容.....

    转载请注明原文地址: https://ju.6miu.com/read-15755.html

    最新回复(0)