日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构_基础结构

發布時間:2024/1/17 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构_基础结构 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
數據結構_基礎結構

鏈表

鏈表是一種數據結構,相對于數組而言,插入和刪除的開銷比較小,而查找的代價較大.以下我們實現雙向鏈表:

public class MyList<T> {private Node<T> head;private Node<T> tail;private int size;//初始化頭結點和尾節點public MyList(){head=new Node<T>(null, null, tail);tail=new Node<T>(null, head, null);}//獲得大小public int size(){return size;}//增加方法public void add(T x){this.addBefore(tail, x);}public void addBefore(int index,T x){this.addBefore(this.getNode(index), x);}public void addBefore(Node<T> p,T x){Node<T> newNode=new Node<T>(x, p.prev, p);newNode.prev.next=newNode;p.prev=newNode;size++;}//查找方法 效率應該高點public Node<T> getNode(int index){Node<T> p;if(index<size()/2){p=head.next;for(int i=0;i<index;i++){p=p.next;}}else{p=tail;for(int i=size();i>index;i--){p=p.prev;}}return p;}//刪除 public T remove(int index){return remove(this.getNode(index));}public T remove(Node<T> p){p.next.prev=p.prev;p.prev.next=p.next;size--;return p.data;}//修改public void set(int index,T newData){Node<T> p=this.getNode(index);p.data=newData;}//顯示全部public void show(){if(size()>0){Node<T> p=head.next;while(p!=tail){System.out.println(p.data);p=p.next;}}}//節點類private static class Node<T>{public T data;public Node<T> prev;public Node<T> next;public Node(T d,Node<T> p,Node<T> n){data=d;prev=p;next=n;}}}

在我寫的這個雙向鏈表中頭節點head和尾節點tail是空出來的,不存放任何數據.若是要改造成雙向循環鏈表的話就不合適了,比如最后一個元素要next三下越過tail和head才循環到第一個元素.
于是稍作改造(注意空指針),雙向循環鏈表:

package com.fredal.structure;public class MyCList<T> {private Node<T> head;private Node<T> tail;private int size;// 初始化頭結點和尾節點public MyCList() {head = new Node<T>(null, tail, tail);tail = new Node<T>(null, head, head);// 解決空指針head.prev = tail;head.next = tail;tail.prev = head;tail.next = head;}// 獲得大小public int size() {return size;}// 增加方法public void add(T x) {if (head.data == null) {head.data = x;size++;} else if (tail.data == null) {tail.data = x;size++;} else {Node<T> p = this.addBefore(head, x);tail = p;}}public Node<T> addBefore(int index, T x) {return this.addBefore(this.getNode(index), x);}public Node<T> addBefore(Node<T> p, T x) {Node<T> newNode = new Node<T>(x, p.prev, p);newNode.prev.next = newNode;p.prev = newNode;size++;return newNode;}// 查找方法 效率應該高點public Node<T> getNode(int index) {Node<T> p;if (index < size() / 2) {p = head;for (int i = 0; i < index; i++) {p = p.next;}} else {p = tail;for (int i = size() - 1; i > index; i--) {p = p.prev;}}return p;}// 刪除public T remove(int index) {return remove(this.getNode(index));}public T remove(Node<T> p) {p.next.prev = p.prev;p.prev.next = p.next;size--;if (p == head) {head = p.next;}if (p == tail) {tail = p.prev;}return p.data;}// 修改public void set(int index, T newData) {Node<T> p = this.getNode(index);p.data = newData;}// 顯示全部public void show() {if (size() > 0) {Node<T> p = head;do {System.out.println(p.data);p = p.next;} while (p != head);}}// 節點類private static class Node<T> {private T data;private Node<T> prev;private Node<T> next;public Node(T d, Node<T> p, Node<T> n) {data = d;prev = p;next = n;}}}

棧Stack是一個表,先進后出,有兩種實現方式:由鏈表或者數組來實現.這里我們選用數組實現做例子,比較簡單:

package com.fredal.structure;public class MyStack<T> {// Java 不支持泛型數組,如需使用,請使用Java提供的容器private Object[] stack;// 棧的默認初始大小private static final int INIT_SIZE = 2;// 棧頂索引private int index;public MyStack() {stack = new Object[INIT_SIZE];index = -1;}/*** 構造方法* * @param initSize* 棧的初始大小*/public MyStack(int initSize) {if (initSize < 0) {throw new IllegalArgumentException();}stack = new Object[initSize];index = -1;}/*** 出棧操作* * @return 棧頂對象*/public T pop() {if (!isEmpty()) {T temp = peek();stack[index--] = null;return temp;}return null;}/*** 入棧操作* * @param obj* 等待入棧的對象*/public void push(T obj) {if (isFull()) {Object[] temp = stack;// 擴容操作,和arraylist的一樣,如果棧滿,則創建空間為當前棧空間兩倍的棧stack = new Object[2 * stack.length];System.arraycopy(temp, 0, stack, 0, temp.length);}stack[++index] = obj;}/*** 查看棧頂對象* * @return 棧頂對象*/public T peek() {if (!isEmpty()) {return (T) stack[index];}return null;}/*** 查看棧是否為空* * @return 如果棧為空返回true,否則返回false*/public boolean isEmpty() {return index == -1;}/*** 查看棧是否滿* * @return 如果棧滿返回true,否則返回false*/public boolean isFull() {return index >= stack.length - 1;} }

應用的比較多的一個是檢測平衡符號,這個比較簡單,就是按順序push按順序pop看看是否相同.另外一個是逆波蘭表達式,寫一段代碼,包括中序表達式轉化為逆波蘭式,以及逆波蘭式的計算:

package com.fredal.structure;import java.util.ArrayList; import java.util.List; import java.util.Stack;public class Nbl {private static Stack s1 = new Stack();// 逆波蘭表達式的棧private static Stack s2 = new Stack();// 運算棧//將字符串轉化成中序listpublic static List<String> format(String s) {List<String> ls = new ArrayList<String>();// 存儲中序表達式int i = 0;String str;char c;do {if ((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57) {ls.add("" + c);i++;} else {str = "";while (i < s.length() && (c = s.charAt(i)) >= 48&& (c = s.charAt(i)) <= 57) {str += c;i++;}ls.add(str);}} while (i < s.length());return ls;}//將中序表達式轉化為逆波蘭式public static List<String> parse(List<String> ls) {List<String> lss = new ArrayList<String>();for (String ss : ls) {if (ss.matches("\\d+")) {lss.add(ss);// 不是運算符就加入list} else if (ss.equals("(")) {s1.push(ss);//碰到"("直接進棧} else if(ss.equals(")")){while(!s1.peek().equals("(")){//將直到"("的符號全彈出lss.add((String) s1.pop());//加入list}s1.pop();//彈出"("}else{while(s1.size()!=0&&getValue((String) s1.peek())>=getValue(ss)){//比它優先級高的全彈出lss.add((String) s1.pop());}s1.push(ss);}}while(s1.size()!=0){//結束之后全部彈出lss.add((String) s1.pop()); }return lss;}//獲取運算符優先級private static int getValue(String s) {if (s.equals("+")) {return 1;} else if (s.equals("-")) {return 1;} else if (s.equals("*")) {return 2;} else if (s.equals("/")) {return 2;}return 0;}// 對逆波蘭表達式進行求值public static int calculate(List<String> ls) {for (String s : ls) {if (s.matches("\\d+")) {s2.push(s);// 不是運算符就入棧} else {int b = Integer.parseInt((String) s2.pop());int a = Integer.parseInt((String) s2.pop());if (s.equals("+")) {a = a + b;} else if (s.equals("-")) {a = a - b;} else if (s.equals("*")) {a = a * b;} else if (s.equals("/")) {a = a / b;}s2.push("" + a);}}return Integer.parseInt((String) s2.pop());} }

隊列

隊列(queue)也是表,使用隊列時插入在一端進行而刪除在另一端進行.
基本操作是enqueue入隊,在表的末端插入元素.dequeue出隊,刪除并返回表的開頭.
用數組實現隊列會有潛在問題,就是隊列滿了之后在入隊一次back的下標會指向不存在的位置,解決這個問題我們用循環隊列的方式.

package com.fredal.structure;public class MyQueue<T> {private int INITIAL_SIZE=10;private int capacity;//數組長度private Object[] elementData;private int front=0;private int back=0;public MyQueue(){capacity=INITIAL_SIZE;elementData=new Object[capacity];}//以一個初始化元素來創建循環隊列public MyQueue(T element){this();elementData[0]=element;back++;}//以指定長度的數組來創建循環隊列 public MyQueue(T element,int initSize){ this.capacity=initSize; elementData=new Object[capacity]; elementData[0]=element; back++; } //判斷循環隊列是否為空public boolean isEmpty(){return back==front&&elementData[back]==null;}//獲取循環隊列的大小public int length(){if(isEmpty()){return 0;}else{return back>front?back-front:capacity-(front-back);} }//插入隊列public void add(T element){if(back==front&&elementData[front]!=null){throw new IndexOutOfBoundsException("隊列已滿");}elementData[back++]=element;//back指針到頭就循環back=(back==capacity)?0:back;}//移出隊列public T remove(){if(isEmpty()){throw new IndexOutOfBoundsException("隊列已空");}T oldValue=(T)elementData[front];elementData[front++]=null;//如果front到頭 就循環front=front==capacity?0:front;return oldValue;}//顯示public void show(){if(isEmpty()){System.out.println("[]");}else{if(front<back){StringBuilder sb=new StringBuilder("["); for(int i=front;i<back;i++){ sb.append(elementData[i].toString()+","); } int len=sb.length(); String s= sb.delete(len-1, len).append("]").toString(); System.out.println(s);}else{StringBuilder sb=new StringBuilder("["); for(int i=front;i<capacity;i++){ sb.append(elementData[i].toString()+","); } for(int i=0;i<back;i++){ sb.append(elementData[i].toString()+","); } int len=sb.length(); String s= sb.delete(len-1, len).append("]").toString(); System.out.println(s);}}} }

先說二叉樹,二叉樹表示每個節點都不能有多于兩個的兒子.二叉樹的一個重要應用是二叉排序樹ADT:

package com.fredal.structure;public class MyTree<T extends Comparable<T>> {// 節點類private static class BNode<T> {T element;BNode<T> left;BNode<T> right;public BNode(T element) {this(element, null, null);}public BNode(T element, BNode<T> lt, BNode<T> rt) {this.element = element;left = lt;right = rt;}}// 插入節點之后public BNode<T> insert(T x, BNode<T> t) {if (t == null) {return new BNode<T>(x, null, null);}int result = x.compareTo(t.element);if (result < 0)t.left = insert(x, t.left);else if (result > 0)t.right = insert(x, t.right);return t;}// 刪除public BNode<T> remove(T x, BNode<T> t) {if (t == null)return t;int result = x.compareTo(t.element);if (result < 0)t.left = remove(x, t.left);else if (result > 0)t.right = remove(x, t.right);else if (t.left != null && t.right != null) {// 找到了 兩個孩子t.element = findMin(t.right).element;// 雖然是奇怪的刪除策略t.right = remove(t.element, t.right);} else// 找到了 一個孩子或沒有孩子t = (t.left != null) ? t.left : t.right;return t;}// 尋找最小值public BNode<T> findMin(BNode<T> t) {if (t == null)return null;else if (t.left == null)return t;return findMin(t.left);}// 尋找最大值public BNode<T> findMax(BNode<T> t) {if (t == null)return null;else if (t.right == null)return t;return findMax(t.right);}// 獲得樹的高度public int getHeight(BNode<T> t) {int a = 0;int b = 0;if (t.left != null)a = getHeight(t.left);if (t.right != null)b = getHeight(t.right);return (a > b ? a : b) + 1;}// 是否包含某個元素public boolean contains(T x, BNode<T> t) {if (t == null)return false;int result = x.compareTo(t.element);if (result < 0)return contains(x, t.left);else if (result > 0)return contains(x, t.right);elsereturn true;}// 顯示 中序遍歷了public void show(BNode<T> t) {if (t.left != null)show(t.left);System.out.println(t.element);if (t.right != null)show(t.right);} }

還有一個就是所謂的表達式樹,一個正常的表達式構建的表達式樹如果采取中序遍歷就是得到正常的表達式,如果采取后序遍歷呢就會產生上一節說的那個逆波蘭表達式,也叫后綴表達式.
基本上這個樹的作用就是把后序表達式變成中序表達式,之前那段代碼逆作用..
用后綴表達式構建樹,再中序遍歷之

package com.fredal.structure;import java.util.Stack;public class ExpTree<T> {// 節點類private static class BNode<T> {T element;BNode<T> left;BNode<T> right;public BNode(T element) {this(element, null, null);}public BNode(T element, BNode<T> lt, BNode<T> rt) {this.element = element;left = lt;right = rt;}}public BNode<Character> bulid(String exp){char c;BNode<Character> newNode,newLeft,newRight;Stack<BNode<Character>> stack=new Stack<BNode<Character>>();int i=0;int len=exp.length();while(i!=len){while(exp.charAt(i)==' '||exp.charAt(i)=='\t'){i++;}if(i==len) break;c=exp.charAt(i);i++;if(c=='+'||c=='-'||c=='*'||c=='/'){//碰到運算符就把前兩個彈出來重新構建一下樹再入棧newRight=stack.pop();newLeft=stack.pop(); newNode=new BNode<Character>(c, newLeft, newRight);stack.push(newNode);}else{newNode=new BNode<Character>(c);//不是運算符直接入棧stack.push(newNode);}}if(!stack.isEmpty()){return stack.pop();//彈出整棵樹}else{return null;}}//中序輸出public void show(BNode<T> t){if(t!=null){show(t.left);System.out.print(t.element+" ");show(t.right);}}}

表達式樹確實是這樣的,中序遍歷也沒錯,不過要變成正常的表達式仍然是不夠的,還需要考慮優先級去加括號,偷懶方法當然是全部加上括號,這兒不寫了.
然后寫一下多叉樹的實現吧,這兒寫習慣了就給node外面加了一層包裝類,這樣添加的時候還是比較麻煩的,待優化吧.

package com.fredal.structure;import java.util.ArrayList; import java.util.List;public class CTree<T> {// 節點類private static class CNode<T> {T element;CNode<T> parent;// 父節點List<CNode<T>> children = new ArrayList<CNode<T>>();// 孩子節點CNode<T> leftBrother;// 左邊第一個兄弟節點CNode<T> rightBrother;// 右邊第一個兄弟節點public CNode(T t, CNode<T> parent) {element = t;this.parent = parent;}}// 插入public CNode<T> insert(T x, CNode<T> t) {CNode<T> newNode = new CNode<T>(x, t);List<CNode<T>> children = t.children;if (children.size() == 0) {children.add(newNode);} else {CNode<T> lastNode = children.get(children.size() - 1);lastNode.rightBrother = newNode;newNode.leftBrother = lastNode;children.add(newNode);}return newNode;}// 得到根節點public CNode<T> getRoot(CNode<T> t) {while (t.parent != null) {t = t.parent;}return t;}// 得到父節點public CNode<T> getParent(CNode<T> t) {if (t.parent != null) {return t.parent;}return null;}// 得到子節點public List<CNode<T>> getChildren(CNode<T> t) {if (t.children != null) {return t.children;}return null;}// 左節點public CNode<T> getLeftBrother(CNode<T> t) {if (t.leftBrother != null) {return t.leftBrother;}return null;}// 右節點public CNode<T> getRightBrother(CNode<T> t) {if (t.rightBrother != null) {return t.rightBrother;}return null;}// 這個屬于先序遍歷public void show(CNode<T> t) {if (t != null) {System.out.print(t.element + " ");List<CNode<T>> children = t.children;for (int i = 0; i < children.size(); i++) {show(children.get(i));}}} }

散列

散列也叫哈希,會遇到散列沖突問題,解決沖突最常用的是分離鏈接法,簡單來說就是將散列到同一個值的所有元素都插入到一個鏈表中去,來實現吧.

package com.fredal.structure;import java.util.LinkedList; import java.util.List;public class MyHashTable<T> {private static final int DEFAULT_SIZE=100;private int size;private List<T>[] lists;public MyHashTable(int size){this.size=size;lists=new LinkedList[size];//初始化鏈表數組for (int i = 0; i < lists.length; i++) {lists[i]=new LinkedList<T>();}}public MyHashTable(){this(DEFAULT_SIZE);}private int myHash(T x){int hashVal=x.hashCode();hashVal%=lists.length;if(hashVal<0){hashVal+=lists.length;}return hashVal;}//是否包含 使用自帶的就好 ~public boolean contains(T x){List<T> list=lists[myHash(x)];return list.contains(x);}//插入方法public void insert(T x){List<T> list=lists[myHash(x)];if(!list.contains(x)){list.add(x);size++;}}//移除方法public void remove(T x){List<T> list=lists[myHash(x)];if(list.contains(x)){list.remove(x);size--;}} }

這里并沒有考慮再散列,因為場景不復雜,再散列以后再談.
還有一種散列表叫探測散列表,也以后再說...

堆也叫優先隊列,是允許至少下列兩種操作的數據結構,插入和刪除并返回最小值.插入(insert)相當于enqueue(入隊),而刪除最小值(deleteMin)則等價于隊列運算dequeue(出隊).
我們要講的叫二叉堆,除了是一顆完整二叉樹外還要保持其堆序性質.例如最小二叉堆即,最小元在根上,而任意節點都小于它的所有后裔.
我們通過代碼模擬二叉堆

package com.fredal.structure;public class MyHeap<T extends Comparable<? super T>> {private static final int DEFAULT_CAPACITY=10;private int currentSize;//堆元素數量private T[] array;//堆數組public MyHeap(){this(DEFAULT_CAPACITY);}public MyHeap(int capacity){currentSize=0;array=(T[]) new Comparable[capacity+1];}//對已給定的數組建堆初始化public MyHeap(T [] items){currentSize=items.length;array=(T[])new Comparable[(currentSize+2)*11/10];int i=1;for(T item:items){array[i++]=item;}buildHeap();}private void buildHeap() {for(int i=currentSize/2;i>0;i--){percolateDown(i);//下濾}}//下濾 保持堆特性private void percolateDown(int i) {int child;T temp =array[i];for(;i*2<=currentSize;i=child){child=i*2;if(child!=currentSize&& array[child+1].compareTo(array[child])<0){child++;}if(array[child].compareTo(temp)<0){array[i]=array[child];}elsebreak;}array[i]=temp;}//插入操作public void insert(T x){if(currentSize==array.length-1){enlargeArray(array.length*2+1);}//上濾操作int i=++currentSize;for(array[0]=x;x.compareTo(array[i/2])<0;i/=2){array[i]=array[i/2];}array[i]=x;}//擴容操作private void enlargeArray(int newSize){T[] old=array;array=(T[]) new Comparable[newSize];for(int i=0;i<old.length;i++){array[i]=old[i];}}//刪除最小值public T deleteMin(){if(currentSize==0){throw new RuntimeException();}T min=findMin();array[1]=array[currentSize--];//下濾percolateDown(1);return min;}//尋找最小值public T findMin() {if(currentSize==0){throw new RuntimeException();}return array[1];}//顯示public void show(){for(int i=1;i<=currentSize;i++){System.out.print(array[i]+" ");}}}

堆的應用有許多,堆排序是最重要的之一,我們在后面會講到.

鏈表

鏈表是一種數據結構,相對于數組而言,插入和刪除的開銷比較小,而查找的代價較大.以下我們實現雙向鏈表:

public class MyList<T> {private Node<T> head;private Node<T> tail;private int size;//初始化頭結點和尾節點public MyList(){head=new Node<T>(null, null, tail);tail=new Node<T>(null, head, null);}//獲得大小public int size(){return size;}//增加方法public void add(T x){this.addBefore(tail, x);}public void addBefore(int index,T x){this.addBefore(this.getNode(index), x);}public void addBefore(Node<T> p,T x){Node<T> newNode=new Node<T>(x, p.prev, p);newNode.prev.next=newNode;p.prev=newNode;size++;}//查找方法 效率應該高點public Node<T> getNode(int index){Node<T> p;if(index<size()/2){p=head.next;for(int i=0;i<index;i++){p=p.next;}}else{p=tail;for(int i=size();i>index;i--){p=p.prev;}}return p;}//刪除 public T remove(int index){return remove(this.getNode(index));}public T remove(Node<T> p){p.next.prev=p.prev;p.prev.next=p.next;size--;return p.data;}//修改public void set(int index,T newData){Node<T> p=this.getNode(index);p.data=newData;}//顯示全部public void show(){if(size()>0){Node<T> p=head.next;while(p!=tail){System.out.println(p.data);p=p.next;}}}//節點類private static class Node<T>{public T data;public Node<T> prev;public Node<T> next;public Node(T d,Node<T> p,Node<T> n){data=d;prev=p;next=n;}}}

在我寫的這個雙向鏈表中頭節點head和尾節點tail是空出來的,不存放任何數據.若是要改造成雙向循環鏈表的話就不合適了,比如最后一個元素要next三下越過tail和head才循環到第一個元素.
于是稍作改造(注意空指針),雙向循環鏈表:

package com.fredal.structure;public class MyCList<T> {private Node<T> head;private Node<T> tail;private int size;// 初始化頭結點和尾節點public MyCList() {head = new Node<T>(null, tail, tail);tail = new Node<T>(null, head, head);// 解決空指針head.prev = tail;head.next = tail;tail.prev = head;tail.next = head;}// 獲得大小public int size() {return size;}// 增加方法public void add(T x) {if (head.data == null) {head.data = x;size++;} else if (tail.data == null) {tail.data = x;size++;} else {Node<T> p = this.addBefore(head, x);tail = p;}}public Node<T> addBefore(int index, T x) {return this.addBefore(this.getNode(index), x);}public Node<T> addBefore(Node<T> p, T x) {Node<T> newNode = new Node<T>(x, p.prev, p);newNode.prev.next = newNode;p.prev = newNode;size++;return newNode;}// 查找方法 效率應該高點public Node<T> getNode(int index) {Node<T> p;if (index < size() / 2) {p = head;for (int i = 0; i < index; i++) {p = p.next;}} else {p = tail;for (int i = size() - 1; i > index; i--) {p = p.prev;}}return p;}// 刪除public T remove(int index) {return remove(this.getNode(index));}public T remove(Node<T> p) {p.next.prev = p.prev;p.prev.next = p.next;size--;if (p == head) {head = p.next;}if (p == tail) {tail = p.prev;}return p.data;}// 修改public void set(int index, T newData) {Node<T> p = this.getNode(index);p.data = newData;}// 顯示全部public void show() {if (size() > 0) {Node<T> p = head;do {System.out.println(p.data);p = p.next;} while (p != head);}}// 節點類private static class Node<T> {private T data;private Node<T> prev;private Node<T> next;public Node(T d, Node<T> p, Node<T> n) {data = d;prev = p;next = n;}}}

棧Stack是一個表,先進后出,有兩種實現方式:由鏈表或者數組來實現.這里我們選用數組實現做例子,比較簡單:

package com.fredal.structure;public class MyStack<T> {// Java 不支持泛型數組,如需使用,請使用Java提供的容器private Object[] stack;// 棧的默認初始大小private static final int INIT_SIZE = 2;// 棧頂索引private int index;public MyStack() {stack = new Object[INIT_SIZE];index = -1;}/*** 構造方法* * @param initSize* 棧的初始大小*/public MyStack(int initSize) {if (initSize < 0) {throw new IllegalArgumentException();}stack = new Object[initSize];index = -1;}/*** 出棧操作* * @return 棧頂對象*/public T pop() {if (!isEmpty()) {T temp = peek();stack[index--] = null;return temp;}return null;}/*** 入棧操作* * @param obj* 等待入棧的對象*/public void push(T obj) {if (isFull()) {Object[] temp = stack;// 擴容操作,和arraylist的一樣,如果棧滿,則創建空間為當前棧空間兩倍的棧stack = new Object[2 * stack.length];System.arraycopy(temp, 0, stack, 0, temp.length);}stack[++index] = obj;}/*** 查看棧頂對象* * @return 棧頂對象*/public T peek() {if (!isEmpty()) {return (T) stack[index];}return null;}/*** 查看棧是否為空* * @return 如果棧為空返回true,否則返回false*/public boolean isEmpty() {return index == -1;}/*** 查看棧是否滿* * @return 如果棧滿返回true,否則返回false*/public boolean isFull() {return index >= stack.length - 1;} }

應用的比較多的一個是檢測平衡符號,這個比較簡單,就是按順序push按順序pop看看是否相同.另外一個是逆波蘭表達式,寫一段代碼,包括中序表達式轉化為逆波蘭式,以及逆波蘭式的計算:

package com.fredal.structure;import java.util.ArrayList; import java.util.List; import java.util.Stack;public class Nbl {private static Stack s1 = new Stack();// 逆波蘭表達式的棧private static Stack s2 = new Stack();// 運算棧//將字符串轉化成中序listpublic static List<String> format(String s) {List<String> ls = new ArrayList<String>();// 存儲中序表達式int i = 0;String str;char c;do {if ((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57) {ls.add("" + c);i++;} else {str = "";while (i < s.length() && (c = s.charAt(i)) >= 48&& (c = s.charAt(i)) <= 57) {str += c;i++;}ls.add(str);}} while (i < s.length());return ls;}//將中序表達式轉化為逆波蘭式public static List<String> parse(List<String> ls) {List<String> lss = new ArrayList<String>();for (String ss : ls) {if (ss.matches("\\d+")) {lss.add(ss);// 不是運算符就加入list} else if (ss.equals("(")) {s1.push(ss);//碰到"("直接進棧} else if(ss.equals(")")){while(!s1.peek().equals("(")){//將直到"("的符號全彈出lss.add((String) s1.pop());//加入list}s1.pop();//彈出"("}else{while(s1.size()!=0&&getValue((String) s1.peek())>=getValue(ss)){//比它優先級高的全彈出lss.add((String) s1.pop());}s1.push(ss);}}while(s1.size()!=0){//結束之后全部彈出lss.add((String) s1.pop()); }return lss;}//獲取運算符優先級private static int getValue(String s) {if (s.equals("+")) {return 1;} else if (s.equals("-")) {return 1;} else if (s.equals("*")) {return 2;} else if (s.equals("/")) {return 2;}return 0;}// 對逆波蘭表達式進行求值public static int calculate(List<String> ls) {for (String s : ls) {if (s.matches("\\d+")) {s2.push(s);// 不是運算符就入棧} else {int b = Integer.parseInt((String) s2.pop());int a = Integer.parseInt((String) s2.pop());if (s.equals("+")) {a = a + b;} else if (s.equals("-")) {a = a - b;} else if (s.equals("*")) {a = a * b;} else if (s.equals("/")) {a = a / b;}s2.push("" + a);}}return Integer.parseInt((String) s2.pop());} }

隊列

隊列(queue)也是表,使用隊列時插入在一端進行而刪除在另一端進行.
基本操作是enqueue入隊,在表的末端插入元素.dequeue出隊,刪除并返回表的開頭.
用數組實現隊列會有潛在問題,就是隊列滿了之后在入隊一次back的下標會指向不存在的位置,解決這個問題我們用循環隊列的方式.

package com.fredal.structure;public class MyQueue<T> {private int INITIAL_SIZE=10;private int capacity;//數組長度private Object[] elementData;private int front=0;private int back=0;public MyQueue(){capacity=INITIAL_SIZE;elementData=new Object[capacity];}//以一個初始化元素來創建循環隊列public MyQueue(T element){this();elementData[0]=element;back++;}//以指定長度的數組來創建循環隊列 public MyQueue(T element,int initSize){ this.capacity=initSize; elementData=new Object[capacity]; elementData[0]=element; back++; } //判斷循環隊列是否為空public boolean isEmpty(){return back==front&&elementData[back]==null;}//獲取循環隊列的大小public int length(){if(isEmpty()){return 0;}else{return back>front?back-front:capacity-(front-back);} }//插入隊列public void add(T element){if(back==front&&elementData[front]!=null){throw new IndexOutOfBoundsException("隊列已滿");}elementData[back++]=element;//back指針到頭就循環back=(back==capacity)?0:back;}//移出隊列public T remove(){if(isEmpty()){throw new IndexOutOfBoundsException("隊列已空");}T oldValue=(T)elementData[front];elementData[front++]=null;//如果front到頭 就循環front=front==capacity?0:front;return oldValue;}//顯示public void show(){if(isEmpty()){System.out.println("[]");}else{if(front<back){StringBuilder sb=new StringBuilder("["); for(int i=front;i<back;i++){ sb.append(elementData[i].toString()+","); } int len=sb.length(); String s= sb.delete(len-1, len).append("]").toString(); System.out.println(s);}else{StringBuilder sb=new StringBuilder("["); for(int i=front;i<capacity;i++){ sb.append(elementData[i].toString()+","); } for(int i=0;i<back;i++){ sb.append(elementData[i].toString()+","); } int len=sb.length(); String s= sb.delete(len-1, len).append("]").toString(); System.out.println(s);}}} }

先說二叉樹,二叉樹表示每個節點都不能有多于兩個的兒子.二叉樹的一個重要應用是二叉排序樹ADT:

package com.fredal.structure;public class MyTree<T extends Comparable<T>> {// 節點類private static class BNode<T> {T element;BNode<T> left;BNode<T> right;public BNode(T element) {this(element, null, null);}public BNode(T element, BNode<T> lt, BNode<T> rt) {this.element = element;left = lt;right = rt;}}// 插入節點之后public BNode<T> insert(T x, BNode<T> t) {if (t == null) {return new BNode<T>(x, null, null);}int result = x.compareTo(t.element);if (result < 0)t.left = insert(x, t.left);else if (result > 0)t.right = insert(x, t.right);return t;}// 刪除public BNode<T> remove(T x, BNode<T> t) {if (t == null)return t;int result = x.compareTo(t.element);if (result < 0)t.left = remove(x, t.left);else if (result > 0)t.right = remove(x, t.right);else if (t.left != null && t.right != null) {// 找到了 兩個孩子t.element = findMin(t.right).element;// 雖然是奇怪的刪除策略t.right = remove(t.element, t.right);} else// 找到了 一個孩子或沒有孩子t = (t.left != null) ? t.left : t.right;return t;}// 尋找最小值public BNode<T> findMin(BNode<T> t) {if (t == null)return null;else if (t.left == null)return t;return findMin(t.left);}// 尋找最大值public BNode<T> findMax(BNode<T> t) {if (t == null)return null;else if (t.right == null)return t;return findMax(t.right);}// 獲得樹的高度public int getHeight(BNode<T> t) {int a = 0;int b = 0;if (t.left != null)a = getHeight(t.left);if (t.right != null)b = getHeight(t.right);return (a > b ? a : b) + 1;}// 是否包含某個元素public boolean contains(T x, BNode<T> t) {if (t == null)return false;int result = x.compareTo(t.element);if (result < 0)return contains(x, t.left);else if (result > 0)return contains(x, t.right);elsereturn true;}// 顯示 中序遍歷了public void show(BNode<T> t) {if (t.left != null)show(t.left);System.out.println(t.element);if (t.right != null)show(t.right);} }

還有一個就是所謂的表達式樹,一個正常的表達式構建的表達式樹如果采取中序遍歷就是得到正常的表達式,如果采取后序遍歷呢就會產生上一節說的那個逆波蘭表達式,也叫后綴表達式.
基本上這個樹的作用就是把后序表達式變成中序表達式,之前那段代碼逆作用..
用后綴表達式構建樹,再中序遍歷之

package com.fredal.structure;import java.util.Stack;public class ExpTree<T> {// 節點類private static class BNode<T> {T element;BNode<T> left;BNode<T> right;public BNode(T element) {this(element, null, null);}public BNode(T element, BNode<T> lt, BNode<T> rt) {this.element = element;left = lt;right = rt;}}public BNode<Character> bulid(String exp){char c;BNode<Character> newNode,newLeft,newRight;Stack<BNode<Character>> stack=new Stack<BNode<Character>>();int i=0;int len=exp.length();while(i!=len){while(exp.charAt(i)==' '||exp.charAt(i)=='\t'){i++;}if(i==len) break;c=exp.charAt(i);i++;if(c=='+'||c=='-'||c=='*'||c=='/'){//碰到運算符就把前兩個彈出來重新構建一下樹再入棧newRight=stack.pop();newLeft=stack.pop(); newNode=new BNode<Character>(c, newLeft, newRight);stack.push(newNode);}else{newNode=new BNode<Character>(c);//不是運算符直接入棧stack.push(newNode);}}if(!stack.isEmpty()){return stack.pop();//彈出整棵樹}else{return null;}}//中序輸出public void show(BNode<T> t){if(t!=null){show(t.left);System.out.print(t.element+" ");show(t.right);}}}

表達式樹確實是這樣的,中序遍歷也沒錯,不過要變成正常的表達式仍然是不夠的,還需要考慮優先級去加括號,偷懶方法當然是全部加上括號,這兒不寫了.
然后寫一下多叉樹的實現吧,這兒寫習慣了就給node外面加了一層包裝類,這樣添加的時候還是比較麻煩的,待優化吧.

package com.fredal.structure;import java.util.ArrayList; import java.util.List;public class CTree<T> {// 節點類private static class CNode<T> {T element;CNode<T> parent;// 父節點List<CNode<T>> children = new ArrayList<CNode<T>>();// 孩子節點CNode<T> leftBrother;// 左邊第一個兄弟節點CNode<T> rightBrother;// 右邊第一個兄弟節點public CNode(T t, CNode<T> parent) {element = t;this.parent = parent;}}// 插入public CNode<T> insert(T x, CNode<T> t) {CNode<T> newNode = new CNode<T>(x, t);List<CNode<T>> children = t.children;if (children.size() == 0) {children.add(newNode);} else {CNode<T> lastNode = children.get(children.size() - 1);lastNode.rightBrother = newNode;newNode.leftBrother = lastNode;children.add(newNode);}return newNode;}// 得到根節點public CNode<T> getRoot(CNode<T> t) {while (t.parent != null) {t = t.parent;}return t;}// 得到父節點public CNode<T> getParent(CNode<T> t) {if (t.parent != null) {return t.parent;}return null;}// 得到子節點public List<CNode<T>> getChildren(CNode<T> t) {if (t.children != null) {return t.children;}return null;}// 左節點public CNode<T> getLeftBrother(CNode<T> t) {if (t.leftBrother != null) {return t.leftBrother;}return null;}// 右節點public CNode<T> getRightBrother(CNode<T> t) {if (t.rightBrother != null) {return t.rightBrother;}return null;}// 這個屬于先序遍歷public void show(CNode<T> t) {if (t != null) {System.out.print(t.element + " ");List<CNode<T>> children = t.children;for (int i = 0; i < children.size(); i++) {show(children.get(i));}}} }

散列

散列也叫哈希,會遇到散列沖突問題,解決沖突最常用的是分離鏈接法,簡單來說就是將散列到同一個值的所有元素都插入到一個鏈表中去,來實現吧.

package com.fredal.structure;import java.util.LinkedList; import java.util.List;public class MyHashTable<T> {private static final int DEFAULT_SIZE=100;private int size;private List<T>[] lists;public MyHashTable(int size){this.size=size;lists=new LinkedList[size];//初始化鏈表數組for (int i = 0; i < lists.length; i++) {lists[i]=new LinkedList<T>();}}public MyHashTable(){this(DEFAULT_SIZE);}private int myHash(T x){int hashVal=x.hashCode();hashVal%=lists.length;if(hashVal<0){hashVal+=lists.length;}return hashVal;}//是否包含 使用自帶的就好 ~public boolean contains(T x){List<T> list=lists[myHash(x)];return list.contains(x);}//插入方法public void insert(T x){List<T> list=lists[myHash(x)];if(!list.contains(x)){list.add(x);size++;}}//移除方法public void remove(T x){List<T> list=lists[myHash(x)];if(list.contains(x)){list.remove(x);size--;}} }

這里并沒有考慮再散列,因為場景不復雜,再散列以后再談.
還有一種散列表叫探測散列表,也以后再說...

堆也叫優先隊列,是允許至少下列兩種操作的數據結構,插入和刪除并返回最小值.插入(insert)相當于enqueue(入隊),而刪除最小值(deleteMin)則等價于隊列運算dequeue(出隊).
我們要講的叫二叉堆,除了是一顆完整二叉樹外還要保持其堆序性質.例如最小二叉堆即,最小元在根上,而任意節點都小于它的所有后裔.
我們通過代碼模擬二叉堆

package com.fredal.structure;public class MyHeap<T extends Comparable<? super T>> {private static final int DEFAULT_CAPACITY=10;private int currentSize;//堆元素數量private T[] array;//堆數組public MyHeap(){this(DEFAULT_CAPACITY);}public MyHeap(int capacity){currentSize=0;array=(T[]) new Comparable[capacity+1];}//對已給定的數組建堆初始化public MyHeap(T [] items){currentSize=items.length;array=(T[])new Comparable[(currentSize+2)*11/10];int i=1;for(T item:items){array[i++]=item;}buildHeap();}private void buildHeap() {for(int i=currentSize/2;i>0;i--){percolateDown(i);//下濾}}//下濾 保持堆特性private void percolateDown(int i) {int child;T temp =array[i];for(;i*2<=currentSize;i=child){child=i*2;if(child!=currentSize&& array[child+1].compareTo(array[child])<0){child++;}if(array[child].compareTo(temp)<0){array[i]=array[child];}elsebreak;}array[i]=temp;}//插入操作public void insert(T x){if(currentSize==array.length-1){enlargeArray(array.length*2+1);}//上濾操作int i=++currentSize;for(array[0]=x;x.compareTo(array[i/2])<0;i/=2){array[i]=array[i/2];}array[i]=x;}//擴容操作private void enlargeArray(int newSize){T[] old=array;array=(T[]) new Comparable[newSize];for(int i=0;i<old.length;i++){array[i]=old[i];}}//刪除最小值public T deleteMin(){if(currentSize==0){throw new RuntimeException();}T min=findMin();array[1]=array[currentSize--];//下濾percolateDown(1);return min;}//尋找最小值public T findMin() {if(currentSize==0){throw new RuntimeException();}return array[1];}//顯示public void show(){for(int i=1;i<=currentSize;i++){System.out.print(array[i]+" ");}}}

堆的應用有許多,堆排序是最重要的之一,我們在后面會講到.
更多文章與相關下載請點擊(http://langgengxin.com/2016/01/29/Structure-2/)

posted on 2016-01-14 08:39 fredal 閱讀(...) 評論(...) 編輯 收藏

轉載于:https://www.cnblogs.com/fredal/p/Structure_2.html

總結

以上是生活随笔為你收集整理的数据结构_基础结构的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。