java数据结构

来源:互联网 发布:交大知行大厦六层 编辑:程序博客网 时间:2024/05/29 19:52

一:概念:

数据结构是对计算机内存(或磁盘)中数据的一种安排,研究数据的逻辑结构;数据的物理存储结构;对数据的操作(或算法),包括数组、链表、二叉树、栈、堆、哈希表,图,队列等。

二:算法:

1、数组

冒泡排序    public static int[] maopao(int[] arr) {        for (int i = arr.length - 1; i > 1; i--) {            for (int j = 0; j < i; j++) {                int temp = 0;                if (arr[j] > arr[j + 1]) {                    temp = arr[j];                    arr[j] = arr[j + 1];                    arr[j + 1] = temp;                }            }        }        return arr;    }

选择排序public static int[] xuanze(int[] arr){        for (int i = 0; i < arr.length - 1; i++) {            for (int j = i + 1; j < arr.length; j++) {                int temp = 0;                if (arr[i] > arr[j]) {                    temp = arr[i];                    arr[i] = arr[j];                    arr[j] = temp;                }            }        }        return arr;    }
插入排序public static int[] insertSort(int[] array) {        if (array == null || array.length < 2) {            return array;        }                // 取数组的第二个元素开始作为要插入的数        for (int i = 1; i < array.length; i++) {            int a = array[i];// 要插入的数            // 把该数之前的所有数作为一个数组遍历比较            for (int j = i - 1; j >= 0; j--) {                if (array[j] > a) { // a之前的那个数大于a,就把之前的那个数往后移                    array[j + 1] = array[j];                    array[j] = a;                } else {                    break;                }            }        }        return array;    }


2、栈

栈为后进先出(Last In First Out)的线性表,简称为LIFO表。

eg:判断“[()]{}{[()[]()]()}”中的括号是否配对

public static boolean testStack() {        String string = "[()]{}{[()[]()]()}";        Stack<String> stack = new Stack<String>();        while (!string.isEmpty()) {            String s = string.substring(0, 1);// 取出首字符            string = string.substring(1);// string取剩下的字符串            if ("[".equals(s) || "(".equals(s) || "{".equals(s)) {                stack.push(s);            } else if ("]".equals(s) || ")".equals(s) || "}".equals(s)) {                if (stack.isEmpty()) {// 取到右括号,如果栈中为空,说明不成对                    return false;                } else { // 判断取出的第一个是否成对                    String leftString = stack.pop();                    if ("]".equals(s)) {                        if (!"[".equals(leftString)) {                            return false;                        }                    }                    if (")".equals(s)) {                        if (!"(".equals(leftString)) {                            return false;                        }                    }                    if ("}".equals(s)) {                        if (!"{".equals(leftString)) {                            return false;                        }                    }                }                            }        }        return stack.isEmpty();    }


3、队列

队列为先进先出(First In First Out)的线性表,简称为FIFO表。队尾插入,队头删除。

操作:peek();获取不删除            pool();获取并删除

eg:反转栈中的数据:    @SuppressWarnings("rawtypes")    private static void reverseStack() {        Stack stack = new Stack();        stack.push("he");        stack.push("saw");        stack.push("a");        stack.push("racecar");        Queue rev = new LinkedList();        while (stack.size() > 0) {            rev.offer(stack.pop());        }        while (rev.size() > 0) {            stack.push(rev.poll());        }        System.out.println(stack);    }


4、链表

线性表:除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的,线性表有两种存储方式,一种是顺序存储结构,另一种是链式存储结构。

顺序存储结构:元素相邻,内存地址也相邻,如数组。
链式存储结构:元素相邻,内存地址不一定相邻。
eg:
存储3个元素(3,7,11),那么相邻元素为:3和7,7和11。
a、顺序存储,如使用数组,那么在数组中相邻元素对应的内存地址也相邻,假设为1,2,3。因为数组存储元素在内存中是挨着放的。
b、链式存储,在内存中,3,7,11可能分别存在了地址为1,3,4的内存中,然后指针从1指向3,从3指向4,那么对于相邻元素3和7,他们在内存中的地址是不相邻的,中间隔着2。


链表:其实就是用链式来存储的线性表。根据指针域的不同,链表分为单向链表、双向链表、循环链表等等

单链表

单链表中每个元素包含两个域,值域(自己的值)和指针域,我们把这样的元素称之为节点。每个节点的指针域内有一个指针,指向下一个节点,而最后一个节点则指向一个空值。单链表只能从前往后遍历。


单链表的实现:

import java.util.Stack;import com.creditcloud.creditmarket.A;public class Test3 {public Node head;public Node current;class Node { // 创造链表的结点类public int data; // 值域public Node next; // 指针域(指向的也是一个结点)public Node(int data) {this.data = data;}}// 添加结点public void add(int data) {if (null == head) { // 如果还没有头结点,先创建head = new Node(data); // (栈内存中head变量指向了堆内存的第一个结点A,A包含了data和next)current = head; // (栈内存中current变量指向了堆内存的第一个结点A,A包含了data和next)} else {current.next = new Node(data);// 添加到当前结点的下一个结点// (改变了current的next,也就是改变了current指向的A中的next,所以此时,head的next已经有值,A的next指向了一个新的结点B)current = current.next; // 当前结点指向新的节点。 (把B赋给current,此时current指向了B)}}// 方法重载:向链表中添加结点public void add(Node node) {if (node == null) {return;}if (head == null) {head = node;current = head;} else {current.next = node;current = current.next;}}// 打印public void print(Node node) { // 从传入的节点位置开始打印if (null == node) {return;}current = node;while (null != current) {System.out.println(current.data);current = current.next;}}// 获取链表长度public int getLength(Node node) {// 从传入的节点位置开始计算if (null == node) {return 0;}current = node;int length = 0;while (null != current) {current = current.next;length++;}return length;}// 打印链表中倒数第K个结点// 思路:1、遍历链表,链表长度-k的位置// 思路:2、设置结点1和结点2都指向头,结点2先移动k-1个位置,然后两个结点一起移动,直到结点2指向尾,结点1此时指向就是倒数第K个结点public Node findNode(Node head, int k) {if (null == head || k <= 0) {return null;}// 设置两个结点Node firstNode = head;Node secondNode = head;for (int i = 0; i < k - 1; i++) {secondNode = secondNode.next;if (null == secondNode) {System.out.println("k已超过链表长度");return null;}}while (null != secondNode.next) {firstNode = firstNode.next;secondNode = secondNode.next;}return firstNode;}// 找出链表的中间结点// 思路:1、遍历// 2、两个结点,A结点每次走一步,B结点走两步,当B结点走到完,A的位置public Node findMidNode(Node head) {if (null == head) {return null;}Node firstNode = head;Node secondNode = head;while (null != secondNode && null != secondNode.next) {// 说明还没有走完firstNode = firstNode.next;secondNode = secondNode.next.next;}return firstNode;}// 合并两条有序链表,合并后还是有序的public Node mergeLinkList(Node head1, Node head2) {if (null == head1 && null == head2) {return null;}if (null == head1) {return head2;}if (null == head2) {return head1;}// 创建新链表的头和当前指向Node newHead;Node newCurrent;// 先让newHead和newCurrent指向两个链表的最小值if (head1.data < head2.data) {newHead = head1;newCurrent = head1;head1 = head1.next;} else {newHead = head2;newCurrent = head2;head2 = head2.next;}while (null != head1 && null != head2) {if (head1.data < head2.data) {newCurrent.next = head1;newCurrent = newCurrent.next;head1 = head1.next;} else {newCurrent.next = head2;newCurrent = newCurrent.next;head2 = head2.next;}}// 如果其中一条已合并完,则指向剩下的值if (null == head1) {newCurrent.next = head2;}if (null == head2) {newCurrent.next = head1;}return newHead;}// 链表反转(把每一个结点取出来放到最前面当表头)public Node reverseNode(Node head) {if (null == head || null == head.next) {return head;}Node reverseHead = null;Node current = head;Node nextNode = null; // 存储原链表的下一个结点// 反转,就是在current遍历时,不断把当前的结点变为新表头,在改变之前,把现在的表头值(也就是上一个结点(第一个结点的上一个结点是null,所以reverseHead初始值为null))// 作为current.next,所以这些操作之前,需要把原表的current.next保存起来。while (current != null) {nextNode = current.next;// 先保存下一个结点的值current.next = reverseHead; // 把新表头(当前结点的上一个结点)付给current.next;reverseHead = current; // 把当前的结点作为新表头current = nextNode; // 继续遍历原链表的下个结点}return reverseHead;}// 从尾到头打印链表的值// 思路:1、遍历链表把值放入栈,利用栈先进后出的原则。 2、先把该链表反转,再打印。public void printReverse(Node head) {if (null == head) {return;}Node current = head;Stack<Node> stack = new Stack<Test3.Node>();while (null != current) {stack.push(current);current = current.next;}while (stack.size() > 0) {System.out.println(stack.pop().data);}}// 方法:判断单链表是否有环,如果有,返回相遇结点public Node hasCycle(Node head) {if (head == null) {return null;}Node first = head;Node second = head;while (second != null) {first = first.next; // first指针走一步second = second.next.next; // second指针走两步if (first == second) { // 一旦两个指针相遇,说明链表是有环的return first;}}return null;}// 方法:有环链表中,获取环的长度。参数node代表的是相遇的那个结点public int getCycleLength(Node node) {if (head == null) {return 0;}Node current = node; // 去环中相遇的结点,让其走一圈重新回到该点int length = 0;while (current != null) {current = current.next;length++;if (current == node) { // 当current结点走到原点的时候return length;}}return length;}// 方法:获取环的起始点。参数length表示环的长度public Node getCycleStart(Node head, int cycleLength) {if (head == null) {return null;}Node first = head;Node second = head;// 先让second指针走length步for (int i = 0; i < cycleLength; i++) {second = second.next;}// 然后让first指针和second指针同时各走一步while (first != null && second != null) {first = first.next;second = second.next;if (first == second) { // 如果两个指针相遇了,说明这个结点就是环的起始点return first;}}return null;}// 方法:求两个单链表相交的第一个交点public Node getFirstCommonNode(Node head1, Node head2) {if (head1 == null || head == null) {return null;}int length1 = getLength(head1);int length2 = getLength(head2);int lengthDif = 0; // 两个链表长度的差值Node longHead;Node shortHead;// 找出较长的那个链表if (length1 > length2) {longHead = head1;shortHead = head2;lengthDif = length1 - length2;} else {longHead = head2;shortHead = head1;lengthDif = length2 - length1;}// 将较长的那个链表的指针向前走length个距离for (int i = 0; i < lengthDif; i++) {longHead = longHead.next;}// 将两个链表的指针同时向前移动while (longHead != null && shortHead != null) {if (longHead == shortHead) { // 第一个相同的结点就是相交的第一个结点return longHead;}longHead = longHead.next;shortHead = shortHead.next;}return null;}public static void main(String[] args) {Test3 test3 = new Test3();Test3 test33 = new Test3();for (int i = 0; i < 10; i++) {test3.add(i);}for (int j = 0; j < 16; j++) {test33.add(j);}// test3.print(test3.head);// System.out.println(test3.getLength(test3.head));// System.out.println(test3.findNode(test3.head, 6).data);// System.out.println(test3.findMidNode(test3.head).data);// test3.print(test3.mergeLinkList(test3.head, test33.head));// test3.print(test3.reverseNode(test3.head));// test3.printReverse(test3.head);// test3.add(test3.head); --为单链表添加环,并指向头,变成循环链表。}}


双向链表:

双向链表的指针域有两个指针,指向直接前驱和直接后驱,可以双向遍历,查询速度变快。  另外删除也变快,直接通过目标指针找到前驱,把前驱的指针指向后驱,单向链表只能从头遍历去找前驱

//从头部插入public void insertFirst(Object obj){          Data data = new Data(obj);          if(first == null){    //第一和最后都指向data            last = data;            }else{                data.right = first;                first.left = data;//把data的右边指向第一个,第一个的坐标指向data(相互指),这样新的data变成了头。             }            first = data;        }  
//从尾部插入public void insertLast(Object obj){          Data data = new Data(obj);          if(first == null){              first = data;          }else{              last.right = data;              data.left = last;          }          last = data;      } 

// 从头部删除    public Object deleteFirst()        throws Exception {        if (null == first) {            throw new Exception();        }        Data temp = first;        if (null == first.right) {            first = null;            last = null;        } else {            first.right.left = null; // 取出原来的第二个,把它的左边置为null,它就成了第一个。            first = first.right;        }        return temp;    }


// 从尾部删除    public Object deleteLast()        throws Exception {        if (null == first) {            throw new Exception();        }        Data temp = last;        if (null == first.right) {            first = null;            last = null;        } else {            last.left.right = null;            last = last.left;        }        return temp;    }


循环链表:

单循环链表就是单链表基础上让尾指向头,可以单向循环遍历。
双循环链表就是双链表基础上头尾互相指,可以双向循环遍历。



双端链表:

双端链表就是在普通单链表基础上,增加一个对最后结点的引用。(图中只有4个结点,first,last只是引用,存在于栈中)



实现:

import java.util.Stack;import com.creditcloud.creditmarket.A;//双端链表的实现public class Test3 {private class Data {private Object obj;private Data next = null;Data(Object obj) {this.obj = obj;}}private Data first = null;private Data last = null;// 插入首结点(每次插入的都是头)public void insertFirst(Object obj) {Data data = new Data(obj);if (first == null) {last = data;}data.next = first; // 新结点指向上一个頭结点first = data;}// 插入尾结点public void insertLast(Object obj) {Data data = new Data(obj);if (first == null) {first = data;} else {last.next = data; // 上一个last指向新节点,新节点就变成了最后一个节点}last = data; // last变量重新指向添加后的最后一个。}// 删除头结点并返回public Object deleteFirst() throws Exception{if(first == null){throw new Exception("empty");}Data temp = first; //把要删除的先保留,用于返回值if(first.next == null){last = null;}first = first.next;   //把头指向原来第二个节点return temp.obj;   }// 删除尾结点public void deleteLast() throws Exception {if (null == first) {throw new Exception();} else if (null == first.next) {first = null;last = null;} else {Data temp = first;while (null != temp.next) {// 遍历寻找倒数第二个(也就是删除后新的尾部)if (last == temp.next) {temp.next = null;last = temp;break;}temp = temp.next;}}}// 输出public void print() {if (null == first) {System.out.println("空");} else {Data dd = first;while (null != dd) {System.out.println(dd.obj.toString() + "-->");dd = dd.next;}}}}


递归:

// 递归二分查找    public int binSearch(int[] arr, int min, int max, int key) {if (min <= max) {            int mid = (min + max) / 2;            if (arr[mid] == key) {                return mid;            } else if (key < arr[mid]) {                return binSearch(arr, min, mid - 1, key);            } else {                return binSearch(arr, mid + 1, max, key);            }        }        return -1;    }

//递归排序: public void sortArray(int[] array, int m, int n) { // 相当于冒泡排序,m是外循环,n是内循环        if (m > 0) {             //外循环            if (array[n] < array[n - 1]) {                swap(array, n);            }                        if (n >= m) {   //内循环,n=m时,说明已经比完一轮                sortArray(array, m - 1, 1);            } else {                sortArray(array, m, n + 1);            }        }    }        void swap(int[] array, int k) {        int temp = array[k];        array[k] = array[k - 1];        array[k - 1] = temp;    }public static void main(String[] args) {        Demo demo = new Demo();        int[] a = new int[] {3, 5, 1, 2, 8, 33, 22, 11, 0};                demo.sortArray(a, a.length - 1, 1); }


二叉树:

二叉树(BinaryTree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的、分别称作这个根的左子树和右子树的二叉树组成,在任一给定结点上,可以按某种次序执行三个操作:     (1)访问结点本身(N),     (2)遍历该结点的左子树(L),     (3)遍历该结点的右子树(R)。
可以有NLR、LNR、LRN、NRL、RNL、RLN等6种遍历方式。


//二叉树的基本实现public class BinaryTree {private TreeNode root = null;public BinaryTree() {root = new TreeNode(1, "根");}// 创建结点private class TreeNode {private int key = 0;private String data = null;private boolean isVisted = false; // 这是干啥用的?private TreeNode leftChild = null;private TreeNode rightChild = null;public TreeNode() {}public TreeNode(int key, String data) {this.key = key;this.data = data;}}// 创建树public void createBinaryTree() {TreeNode newNodeB = new TreeNode(2, "B");TreeNode newNodeC = new TreeNode(3, "C");TreeNode newNodeD = new TreeNode(4, "D");TreeNode newNodeE = new TreeNode(5, "E");TreeNode newNodeF = new TreeNode(6, "F");root.leftChild = newNodeB;root.rightChild = newNodeC;newNodeB.leftChild = newNodeD;newNodeB.rightChild = newNodeE;newNodeC.leftChild = newNodeF;}// 判断为空public boolean isEmpty() {return root == null;}// 树的高度public int height(TreeNode root) {if (null == root) {return 0;}int i = height(root.leftChild);int j = height(root.rightChild);return (i < j) ? (j + 1) : (i + 1);}// 树的结点数private int size(TreeNode root) {if (root == null) {return 0;} else {return 1 + size(root.leftChild) + size(root.rightChild);}}// 释放某个结点public void destroy(TreeNode subTree) {// 删除根为subTree的子树if (subTree != null) {// 删除左子树destroy(subTree.leftChild);// 删除右子树destroy(subTree.rightChild);// 删除根结点subTree = null;}}// 返回某个结点的双亲结点public TreeNode parent(TreeNode root, TreeNode element) {if (null == root || element == root) {return null;} else {if (root.leftChild == element || root.rightChild == element) {return root;}// 递归判断子节点的子节点TreeNode node = null;if ((node = parent(root.leftChild, element)) != null) {return node;} else if ((node = parent(root.rightChild, element)) != null) {return node;} else {return null;}}}// 前序遍历public void preOrder(TreeNode subTree) {if (subTree != null) {System.err.println(subTree.key + "---------" + subTree.data);preOrder(subTree.leftChild);preOrder(subTree.rightChild);}}// 后续遍历public void lastOrder(TreeNode subTree) {if (subTree != null) {preOrder(subTree.leftChild);preOrder(subTree.rightChild);System.err.println(subTree.key + "---------" + subTree.data);}}// 中序遍历public void inOrder(TreeNode subTree) {if (subTree != null) {inOrder(subTree.leftChild);System.err.println(subTree.key + "---------" + subTree.data);inOrder(subTree.rightChild);}}// 测试实现public static void main(String[] args) {BinaryTree tree = new BinaryTree();// 创建树tree.createBinaryTree();// 树的高度int h = tree.height(tree.root);System.err.println("树高为:" + h);// 结点数int size = tree.size(tree.root);System.err.println("结点数为:" + size);// 前序遍历tree.preOrder(tree.root);// 后序遍历tree.lastOrder(tree.root);}}


堆:
堆数据结构是一种数组对象,它可以被视为一棵完全二叉树,它的特点是父节点的值大于(小于)两个子节点的值(分别称为大顶(根)堆和小顶(根)堆),应用场景包括堆排序,优先队列等。

堆的基本操作
    堆是一棵完全二叉树,高度为O(lg n),其基本操作至多与树的高度成正比。在介绍堆的基本操作之前,先介绍几个基本术语:
 
    A:用于表示堆的数组,下标从1开始,一直到n
 
  PARENT(t):节点t的父节点,即t/2(取整数)
 
  RIGHT(t):节点t的左孩子节点,即:2*t
 
    LEFT(t):节点t的右孩子节点,即:2*t+1
 
    HEAP_SIZE(A):堆A当前的元素数目


保持堆的性质 Heapify(A,t)
     该操作主要用于维持堆的基本性质。假定以RIGHT(t)结点和LEFT(t)结点为根的子树都已经是堆,然后调整以t结点为根的子树,使之成为堆


最大堆的性质:

void Heapify(int A[], int t)Int L=LEFT(t);   //t的左结点位置Int R=RIGHT(t);  //t的右结点位置Int  max; if(L<=HEAP_SIZE(A)) {  largest = A[L] > A[t] ? L : t         } if(R<=HEAP_SIZE(A)) { largest=A[R]>A[largest]?r:largest         //两个if从t, 2*t, 2*t+1中找出最大的一个 }if(largest!=t) {             //t不是最大的 ,说明t不该为根结点   swap(A[t],A[largest]);  //把根结点换最大值   Heapify(A,largest);         //交换后,子树有可能违反最大堆性质   } }

建堆BuildHeap(A,n):

操作主要是将数组A转化成一个大顶堆。思想是,先找到堆的最后一个非叶子节点(就是该结点下面还有叶子——即为第n/2个节点),然后从该节点开始,从后往前逐个调整每个子树,使之称为堆,最终整个数组便是一个堆。子数组A[(n/2)+1..n]中的元素都只是树中的叶子,因此都可以看作是只含有一个元素的堆。

Public void BuildHeap(int A[]) { int i; for(i = HEAP_SIZE(A)/2; i>=1; i--) {     Heapify(A, i);   } } 


堆排序算法:

先用BuildHeapo将数组A[1..n]构造成一个最大堆。因为数组中最大元素在根A[1],则可以通过把它与A[n]交换来达到最终正确的位置。

void HeapSort(int A[]) { BuildHeap(A); for(i=HEAP_SIZE(A),i>1; i--) { swap(A[1],A[i]); HEAP_SIZE(A)=HEAP_SIZE(A)-1; Heapify(A,1); //交换后1位置上的根元素可能违背了最大堆的性质 } }


向堆中插入元素:

向堆中添加一个元素t,同时保持堆的性质。算法思想是,将t放到A的最后,然后从该元素开始,自下向上调整,直至A成为一个大顶堆

void Insert(int A[], int n, int t) {   n++;     //数组中元素个数+1   A[n] = t;   //最后一个元素为插入的t   int p = n;  //插入元素的位置为p   while(p >1 && A[PARENT(p)] < t)            //当元素t的位置>1且t的父节点值小于t   {     A[p] = A[PARENT(p)];   //交换t和t的父节点在数组中对应的值     p = PARENT(p);  //t的新位置代替原父节点的位置,继续循环,直到在堆中找到合适的位置   }   A[p] = t;     //最终t的位置上的换成t的值}


红黑树:(待完成)

原创粉丝点击