zoukankan      html  css  js  c++  java
  • 数据结构与算法(二)

    链表

      链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。

      使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。

      单向链表实现

    public class SingleLinkedList<T>{
    
        private int size;
    
        private Node<T> head;
    
        public SingleLinkedList(){
            size =0;
            head = null;
        }
    
        private class Node<T>{
            private T data;
            private Node next;
    
            public Node(T t){
                data = t;
            }
        }
    
        public T addHead(T t){
            Node node = new Node(t);
            if(head==null){
                head =node;
            }else{
                node.next = head;
                head = node;
            }
            size++;
            return t;
        }
    
    
        public boolean deleteHead(){
            if(isEmpty()){
                return false;
            }
            T value = head.data;
            head = head.next;
            size--;
            return true;
    
        }
    
        public boolean deleteNode(T t){
            boolean value = false;
            if(isEmpty()){
                return value;
            }
            Node  current = head;
            Node previous = head;
            while(!t.equals(current.data)){
                if(current.next==null){
                   value =false;
                   break;
                }else{
                    previous = current;
                    current = current.next;
                }
            }
            if(current == head){
                head = current.next;
                size--;
            }else{
                previous.next = current.next;
                size--;
            }
            return value;
    
        }
    
    
    
        public boolean contain(T t){
            boolean value = false;
            if(isEmpty()){
                return value;
            }
            Node  node = head;
            while(node != null ){
                if(t.equals(node.data)){
                    value =true;
                    break;
                }
                node = node.next;
            }
            return value;
        }
    
        public boolean isEmpty(){
            return size==0;
        }
    
        public static void main(String[] args) {
            SingleLinkedList<String> stringSingleLinkedList = new SingleLinkedList<>();
            stringSingleLinkedList.addHead("asd");
            stringSingleLinkedList.addHead("zcx");
            stringSingleLinkedList.addHead("qwe");
            System.out.println(stringSingleLinkedList.contain("zcx"));
            stringSingleLinkedList.deleteNode("zcx");
            System.out.println(stringSingleLinkedList.contain("zcx"));
            System.out.println(stringSingleLinkedList.isEmpty());
            stringSingleLinkedList.deleteHead();
            System.out.println(stringSingleLinkedList.contain("qwe"));
            System.out.println(stringSingleLinkedList.isEmpty());
            System.out.println(stringSingleLinkedList.contain("asd"));
        }
    }

      利用单项链表实现栈

    public class SingleLinkStack<T> {
    
        private SingleLinkedList<T> stringSingleLinkedList = new SingleLinkedList<>();
    
    
        public void push(T t){
            stringSingleLinkedList.addHead(t);
        }
    
        public boolean poll(){
            return stringSingleLinkedList.deleteHead();
        }
    
        public boolean isEmpty(){
            return stringSingleLinkedList.isEmpty();
        }
    }

      双端链表,注意和双向链表的区别

    public class DoublePointLinkedList<T>{
    
        private int size;
    
        private Node<T> head;
    
        private Node<T> tail;
    
        public DoublePointLinkedList(){
            size =0;
            tail =head = null;
        }
    
        private class Node<T>{
            private T data;
            private Node next;
    
            public Node(T t){
                data = t;
            }
        }
    
        public T addHead(T t){
            Node node = new Node(t);
            if(head==null){
                tail = head =node;
            }else{
                node.next = head;
                head = node;
            }
            size++;
            return t;
        }
    
        public T addTail(T t){
            Node node = new Node(t);
            if(tail == null){
                tail = head =node;
            }else{
                tail.next = node;
                tail = node;
            }
            size++;
            return t;
        }
    
    
        public T deleteHead(){
            if(isEmpty()){
                return null;
            }
            T t = head.data;
            if(head.next==null){
                tail =head =null;
            }else{
                head = head.next;
            }
            size--;
            return t;
    
        }
    
        public boolean deleteNode(T t){
            boolean value = false;
            if(isEmpty()){
                return value;
            }
            Node  current = head;
            Node previous = head;
            while(!t.equals(current.data)){
                if(current.next==null){
                   value =false;
                   break;
                }else{
                    previous = current;
                    current = current.next;
                }
            }
            if(current == head){
                head = current.next;
                size--;
            }else{
                previous.next = current.next;
                size--;
            }
            return value;
    
        }
    
    
    
        public boolean contain(T t){
            boolean value = false;
            if(isEmpty()){
                return value;
            }
            Node  node = head;
            while(node != null ){
                if(t.equals(node.data)){
                    value =true;
                    break;
                }
                node = node.next;
            }
            return value;
        }
    
        public void display(){
            if(isEmpty()){
                return;
            }else {
                Node node = head;
                while(node !=null){
                    System.out.println(node.data);
                    node = node.next;
                }
            }
        }
    
    
        public boolean isEmpty(){
            return size==0;
        }
    
        public static void main(String[] args) {
            DoublePointLinkedList<String> doublePointLinkedList = new DoublePointLinkedList<>();
            doublePointLinkedList.addHead("asd");
            doublePointLinkedList.addHead("zcx");
            doublePointLinkedList.addHead("qwe");
            doublePointLinkedList.addTail("vbn");
            doublePointLinkedList.display();
        }
    }

      利用双端链表实现队列

    public class LinkedListQueue <T>{
    
        private DoublePointLinkedList<T> doublePointLinkedList = new DoublePointLinkedList<T>();
    
        public void add(T t){
            doublePointLinkedList.addTail(t);
        }
    
        public T poll(){
            return doublePointLinkedList.deleteHead();
        }
    
        public static void main(String[] args) {
            LinkedListQueue<String> linkedListQueue = new LinkedListQueue<>();
            linkedListQueue.add("asd");
            linkedListQueue.add("qwe");
            linkedListQueue.add("zxc");
            System.out.println(linkedListQueue.poll());
            System.out.println(linkedListQueue.poll());
            System.out.println(linkedListQueue.poll());
    
        }
    }

      利用链表实现优先级队列

    public class OrderSingleLinkedList<T extends Comparable>{
    
        private int size;
    
        private Node<T> head;
    
        public OrderSingleLinkedList(){
            size =0;
            head = null;
        }
    
        private class Node<T extends Comparable>{
            private T data;
            private Node next;
    
            public Node(T t){
                data = t;
            }
        }
    
        public T addHead(T t){
            Node node = new Node(t);
            if(head==null){
                head =node;
            }else{
                node.next = head;
                head = node;
            }
            size++;
            return t;
        }
    
        public void insert(T t){
            Node node = new Node(t);
            if(isEmpty()){
                head = node;
                size++;
                return;
            }
            Node previous = null;
            Node current = head;
            while(current!=null&& (current.data.compareTo(t)<0)){
                previous = current;
                current =current.next;
            }
            if(previous==null){
                head = node;
                node.next = current;
            }else{
                previous.next = node;
                node.next = current;
            }
            size++;
        }
    
    
        public boolean deleteHead(){
            if(isEmpty()){
                return false;
            }
            head = head.next;
            size--;
            return true;
    
        }
    
        public boolean deleteNode(T t){
            boolean value = false;
            if(isEmpty()){
                return value;
            }
            Node  current = head;
            Node previous = head;
            while(!t.equals(current.data)){
                if(current.next==null){
                   value =false;
                   break;
                }else{
                    previous = current;
                    current = current.next;
                }
            }
            if(current == head){
                head = current.next;
                size--;
            }else{
                previous.next = current.next;
                size--;
            }
            return value;
    
        }
    
    
    
        public boolean contain(T t){
            boolean value = false;
            if(isEmpty()){
                return value;
            }
            Node  node = head;
            while(node != null ){
                if(t.equals(node.data)){
                    value =true;
                    break;
                }
                node = node.next;
            }
            return value;
        }
    
        public void display(){
            if(isEmpty()){
                return;
            }else {
                Node node = head;
                while(node !=null){
                    System.out.println(node.data);
                    node = node.next;
                }
            }
        }
    
        public boolean isEmpty(){
            return size==0;
        }
    
        public static void main(String[] args) {
    
            OrderSingleLinkedList<String> orderSingleLinkedList = new OrderSingleLinkedList<>();
            orderSingleLinkedList.insert("asd");
            orderSingleLinkedList.insert("zcx");
            orderSingleLinkedList.insert("qwe");
            orderSingleLinkedList.insert("awe");
            orderSingleLinkedList.insert("zxs");
            orderSingleLinkedList.display();
    
        }
    }

      双向链表

    public class TwoWayLinkedList<T> {
    
        private Node head;
        private Node tail;
        private int size =0;
    
        public TwoWayLinkedList(){
    
            tail = head =null;
        }
    
        private class Node<T>{
            private T data;
            private Node next;
            private Node previous;
    
            public Node(T t){
                data = t;
            }
        }
    
        public void addHead(T t){
            Node node = new Node(t);
            if(head == null){
                tail = head = node;
            }else{
                head.previous = node;
                node.next = head;
                head = node;
            }
            size++;
        }
    
        public void addTail(T t){
            Node node = new Node(t);
            if(tail ==null){
                head = tail = node;
            }else{
                tail.next = node;
                node.previous = tail;
                tail = node;
            }
            size++;
        }
    
        public T deleteHead(){
            Node<T> temp = head;
            if(head!=null){
                head = head.next;
                T t = temp.data;
                temp.data= null;
                size--;
                return t;
            }else{
                return null;
            }
        }
    
        public void forwardDisplay(){
            Node node = head;
            while (node!= null){
                System.out.println(node.data);
                node = node.next;
            }
        }
        public void backWarddisplay(){
            Node node = tail;
            while (node!= null){
                System.out.println(node.data);
                node = node.previous;
            }
        }
    
        public static void main(String[] args) {
            TwoWayLinkedList<String> twoWayLinkedList = new TwoWayLinkedList<>();
            twoWayLinkedList.addHead("qwe");
            twoWayLinkedList.addHead("asd");
            twoWayLinkedList.addTail("zxc");
            twoWayLinkedList.forwardDisplay();
            System.out.println("------------------");
            twoWayLinkedList.backWarddisplay();
        }
    }

    递归

      一个递归方法每次都是用不同的参数值反复调用自己,当某种参数值使得递归的方法返回,而不再调用自身,这种情况称为边界值,也叫基值。当递归方法返回时,递归过程通过逐渐完成各层方法实例的未执行部分,而从最内层返回到最外层的原始调用处。

      阶乘、汉诺塔、归并排序等都可以用递归来实现,但是要注意任何可以用递归完成的算法用栈都能实现。当我们发现递归的方法效率比较低时,可以考虑用循环或者栈来代替它。

      利用递归首先归并排序

    public class MergeSort {
    
        public static int[] mergeSort(int[] c,int start,int last){
            if(last > start){
                //也可以是(start+last)/2,这样写是为了防止数组长度很大造成两者相加超过int范围,导致溢出
                int mid = start + (last - start)/2;
                mergeSort(c,start,mid);//左边数组排序
                mergeSort(c,mid+1,last);//右边数组排序
                merge(c,start,mid,last);//合并左右数组
            }
            return c;
        }
    
        public static void merge(int[] c,int start,int mid,int last){
            int[] temp = new int[last-start+1];//定义临时数组
            int i = start;//定义左边数组的下标
            int j = mid + 1;//定义右边数组的下标
            int k = 0;
            while(i <= mid && j <= last){
                if(c[i] < c[j]){
                    temp[k++] = c[i++];
                }else{
                    temp[k++] = c[j++];
                }
            }
            //把左边剩余数组元素移入新数组中
            while(i <= mid){
                temp[k++] = c[i++];
            }
            //把右边剩余数组元素移入到新数组中
            while(j <= last){
                temp[k++] = c[j++];
            }
    
            //把新数组中的数覆盖到c数组中
            for(int k2 = 0 ; k2 < temp.length ; k2++){
                c[k2+start] = temp[k2];
            }
        }
    
        public static void main(String[] args) {
            int[] array ={3,2,5,6,8,7,9,1,4};
            Arrays.stream(array).forEach(System.out::println);
            array = mergeSort(array,0,array.length-1);
            Arrays.stream(array).forEach(System.out::println);
        }
    }

      二分查找也可以利用递归实现,并且便于理解但是效率比较低,下面使用循环实现二分查找

    public class TwoPointSelect {
    
    
        public static int twoPoint (int[] array,int key){
            int start = 0;
            int end = array.length-1;
            while(start<=end){
                int middle =(end-start)/2 + start;
                if(key == array[middle]){
                    return middle;
                }
                if(key <array[middle]){
                    end = middle-1;
                }
                if(key >array[middle]){
                    start = middle +1;
                }
            }
            return -1;
        }
    
        public static void main(String[] args) {
            int[] array ={1,2,3,4,5,6,7,8,9};
            System.out.println(twoPoint(array,5));
        }
    }

      树的常用术语

      1、路径:顺着节点的边从一个节点走到另一个节点,所经过的节点的顺序排列就称为“路径”。

      2、根:树顶端的节点称为根。一棵树只有一个根,如果要把一个节点和边的集合称为树,那么从根到其他任何一个节点都必须有且只有一条路径。A是根节点。

      3、父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;B是D的父节点。

      4、子节点:一个节点含有的子树的根节点称为该节点的子节点;D是B的子节点。

      5、兄弟节点:具有相同父节点的节点互称为兄弟节点;比如上图的D和E就互称为兄弟节点。

      6、叶节点:没有子节点的节点称为叶节点,也叫叶子节点,比如上图的H、E、F、G都是叶子节点。

      7、子树:每个节点都可以作为子树的根,它和它所有的子节点、子节点的子节点等都包含在子树中。

      8、节点的层次:从根开始定义,根为第一层,根的子节点为第二层,以此类推。

      9、深度:对于任意节点n,n的深度为从根到n的唯一路径长,根的深度为0;

      10、高度:对于任意节点n,n的高度为从n到一片树叶的最长路径长,所有树叶的高度为0;

      二叉树:树的每个节点最多只能有两个子节点。

      二叉搜索树要求:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

      遍历树是根据一种特定的顺序访问树的每一个节点。比较常用的有前序遍历,中序遍历和后序遍历。而二叉搜索树最常用的是中序遍历。

      1、中序遍历:左子树——》根节点——》右子树

      2、前序遍历:根节点——》左子树——》右子树

      3、后序遍历:左子树——》右子树——》根节点

      树是由边和节点构成,根节点是树最顶端的节点,它没有父节点;二叉树中,最多有两个子节点;某个节点的左子树每个节点都比该节点的关键字值小,右子树的每个节点都比该节点的关键字值大,那么这种树称为二叉搜索树,其查找、插入、删除的时间复杂度都为logN;可以通过前序遍历、中序遍历、后序遍历来遍历树,前序是根节点-左子树-右子树,中序是左子树-根节点-右子树,后序是左子树-右子树-根节点;删除一个节点只需要断开指向它的引用即可;哈夫曼树是二叉树,用于数据压缩算法,最经常出现的字符编码位数最少,很少出现的字符编码位数多一些。  

      二叉搜索树对于某个节点而言,其左子树的节点关键值都小于该节点关键值,右子树的所有节点关键值都大于该节点关键值。二叉搜索树作为一种数据结构,其查找、插入和删除操作的时间复杂度都为O(logn),底数为2。但是我们说这个时间复杂度是在平衡的二叉搜索树上体现的,也就是如果插入的数据是随机的,则效率很高,但是如果插入的数据是有序的,比如从小到大的顺序【10,20,30,40,50】插入到二叉搜索树中,则数据全在右边,而从大到小插入就是全部在左边,这和链表没有任何区别了,这种情况下查找的时间复杂度为O(N),而不是O(logN)。当然这是在最不平衡的条件下,实际情况下,二叉搜索树的效率应该在O(N)和O(logN)之间,这取决于树的不平衡程度。

      那么为了能够以较快的时间O(logN)来搜索一棵树,我们需要保证树总是平衡的(或者大部分是平衡的),也就是说每个节点的左子树节点个数和右子树节点个数尽量相等。红-黑树的就是这样的一棵平衡树,对一个要插入的数据项(删除也是),插入例程要检查会不会破坏树的特征,如果破坏了,程序就会进行纠正,根据需要改变树的结构,从而保持树的平衡。

      红黑树的特征:

      1、节点都有颜色;

      2、在插入和删除的过程中,要遵循保持这些颜色的不同排列规则,具体如下

      ①每个节点不是红色就是黑色的;

      ②根节点总是黑色的;

      ③如果节点是红色的,则它的子节点必须是黑色的(反之不一定),(也就是从每个叶子到根的所有路径上不能有两个连续的红色节点);

      ④从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度);从根节点到叶节点的路径上的黑色节点的数目称为黑色高度,规则 4 另一种表示就是从根到叶节点路径上的黑色高度必须相同。

      注意:新插入的节点颜色总是红色的,这是因为插入一个红色节点比插入一个黑色节点违背红-黑规则的可能性更小,原因是插入黑色节点总会改变黑色高度(违背规则4),但是插入红色节点只有一半的机会会违背规则3(因为父节点是黑色的没事,父节点是红色的就违背规则3)。另外违背规则3比违背规则4要更容易修正。

      具体红黑树的实现可以参考java中TreeMap源码。

      数据结构中树除了以上结构外还有2-3-4树,B树,B+,B-树等,还用用于表示更复杂的关系的图等。

    高级排序

      直接插入排序更适合于原始记录基本有序的集合。这是因为如果记录基本有序,那么直接插入排序时移动的次数就会很少。而希尔排序正式利用了直接排序的这一个特点,希尔排序将数据按一定的步长进行分组,使得记录很快就会达到整体基本有序。

      希尔排序的步长选择是一个数学难题,根据经验通常使用以下公式进行计算:while(step<array.length/3){step = step*3 +1;},每轮排序的步长是动态变化的。demo如下

    public class ShellSort {
    
        public static void shellSort(int[] array){
    
            System.out.println(Arrays.toString(array));
            int step = 1;
            while(step<array.length/3){
                step = step*3 +1;
            }
    
            while(step>0){
                for(int i =step;i<array.length;i++){
                    int temp = array[i];
                    int j =i;
                    while(j > step-1 && temp <= array[j-step]){
                        array[j] = array[j-step];
                        j -= step;
                    }
                    array[j] = temp;
    
                }
                System.out.println(Arrays.toString(array));
                step = (step-1)/3;
            }
            System.out.println(Arrays.toString(array));
    
        }
    
        public static void main(String[] args) {
            int[] array = {4,2,8,9,5,7,6,1,3,10};
            shellSort(array);
        }
    }

      快速排序(Quicksort)是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。demo如下

    public class QuickSort {
    
        //数组array中下标为i和j位置的元素进行交换
        private static void swap(int[] array , int i , int j){
            int temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
    
        private static void recQuickSort(int[] array,int left,int right){
            if(right <= left){
                return;//终止递归
            }else{
    
                int partition = partitionIt(array,left,right);
                recQuickSort(array,left,partition-1);// 对上一轮排序(切分)时,基准元素左边的子数组进行递归
                recQuickSort(array,partition+1,right);// 对上一轮排序(切分)时,基准元素右边的子数组进行递归
            }
        }
    
        private static int partitionIt(int[] array,int left,int right){
            //为什么 j加一个1,而i没有加1,是因为下面的循环判断是从--j和++i开始的.
            //而基准元素选的array[left],即第一个元素,所以左游标从第二个元素开始比较
            int i = left;
            int j = right+1;
            int pivot = array[left];// pivot 为选取的基准元素(头元素)
            int size = right - left + 1;
            if(size >= 3){
                pivot = medianOf3(array, left, right); //数组范围大于3,基准元素选择中间值。
            }
            while(true){
                while(i<right && array[++i] < pivot){}
    
                while(j > 0 && array[--j] > pivot){}
    
                if(i >= j){// 左右游标相遇时候停止, 所以跳出外部while循环
                    break;
                }else{
                    swap(array, i, j);// 左右游标未相遇时停止, 交换各自所指元素,循环继续
                }
            }
            swap(array, left, j);//基准元素和游标相遇时所指元素交换,为最后一次交换
            return j;// 一趟排序完成, 返回基准元素位置(注意这里基准元素已经交换位置了)
        }
    
        public static void sort(int[] array){
            recQuickSort(array, 0, array.length-1);
        }
    
        private static int medianOf3(int[] array,int left,int right){
            int center = (right-left)/2+left;
            if(array[left] > array[right]){ //得到 array[left] < array[right]
                swap(array, left, right);
            }
            if(array[center] > array[right]){ //得到 array[left] array[center] < array[right]
                swap(array, center, right);
            }
            if(array[center] > array[left]){ //得到 array[center] <  array[left] < array[right]
                swap(array, center, left);
            }
    
            return array[left]; //array[left]的值已经被换成三数中的中位数, 将其返回
        }
    
        //测试
        public static void main(String[] args) {
            int[] array = {7,3,5,2,9,8,6,1,4,7};
            sort(array);
            for(int i : array){
                System.out.print(i+" ");
            }
        }
    
    }

      

  • 相关阅读:
    random、shutil、shevle、标准输入输出错误流
    Python中Json 和 pickle 详解
    Python 中的sys、os、os.path模块总结
    Python 中time,calendar,datatime模块总结
    包的概念和导入包的方法
    Python 中模块及其导入方式
    迭代器和生成器
    python 函数参数
    JAVA 配置Path环境变量
    腾讯云服务器简单配置web项目
  • 原文地址:https://www.cnblogs.com/hhhshct/p/10874227.html
Copyright © 2011-2022 走看看