数据结构
一.定义
1数据结构:数据结构是计算机存储、组织数据的方式。
2集合:集合是一组可变数量的数据项(包括0)的组合。
二.数据的逻辑结构
1.线性结构
①特征:集合中必存在唯一的一个“第一个元素”;
集合中必存在唯一的一个“最后的元素”;
除最后一个元素之外,其它元素均有唯一“后继”
除第一个元素之外,其他元素均有唯一“前驱”
②.数组
数组特征:创建数组时,需要固定数组的大小,一旦固定其长度不能修改,数组的长度不能在函数的运行过程中动态的扩充和缩小。
数组中的数据是同一类型的,可以是基本数据类型,引用数据类型。
数组中的各数据是有先后顺序的,它们在内存中按照这个先后顺序连续存放在一起。
数组删除和插入需要花费昂贵的开销,特别是在数组头进行删除和插入操作,需要将数组中所有的素 前移或后移一位。
多维数组的使用:
二维数组
/*
* 数组的使用
*/
public class Array {
/*
* 二维数组的创建、遍历。
*/
@Test
public void Two_dimensionalArray(){
String[][]str = new String[3][5];
for (int i = 0; i < str.length; i++) {
for (int j = 0; j < str[i].length; j++) {
str[i][j]=i+j+"";
}
}
Integer[][] in = {{1,2,3},{4,5,6},{7,8,9,10}};
Character[][] ch =new Character[3][];
ch[0] = new Character[5];
ch[1] = new Character[4];
ch[2] = new Character[3];
doArray(str);
System.out.println("next----");
doArray(in);
System.out.println("next----");
doArray(ch);
}
public static void doArray(Object obj[][]){
for (int i = 0; i < obj.length; i++) {
for (int j = 0; j < obj[i].length; j++) {
System.out.print(obj[i][j]+" ");
}
System.out.println("\r\n");
}
System.out.println("next Method");
for (Object[] objects : obj) {
for (int i = 0; i < objects.length; i++) {
System.out.print(objects[i]+" ");
}
System.out.println("\r\n");
}
}
/*
* 三维数组的创建、遍历。
*/
@Test
public void Three_dimensionalArray(){
String [][][]str = new String[4][3][4];
for (int i = 0; i < str.length; i++) {
for (int j = 0; j < str[i].length; j++) {
for (int j2 = 0; j2 < str[i][j].length; j2++) {
str[i][j][j2]=i+j+j2+"";
}
}
}
for (String[][] strings : str) {
for (String[] strings2 : strings) {
for (String string : strings2) {
System.out.print(string+" ");
}
System.out.println();
}
System.out.println();
}
}
/*
* 四维数组的创建、遍历。
*/
@Test
public void four_dimensionalArray(){
String [][][][]str = new String[3][4][3][4];
for(int i=0; i<str.length;i++){
for(int j = 0; j<str[i].length;j++){
for(int x = 0; x<str[i][j].length;x++){
for (int k = 0; k < str[i][j][x].length; k++) {
str[i][j][x][k] = i+x+j+k+"";
}
}
}
}
for (String[][][] strings : str) {
for (String[][] strings2 : strings) {
for (String[] strings3 : strings2) {
for (String string : strings3) {
System.out.print(string+"");
}
System.out.println("一个二维数组");
}
System.out.println("一个三维数组");
}
System.out.println("一个四位数组");
}
}
}
三维数组又被认为是二维数组的数组,而二维数组也可以认为是一维数组的数组。
N维数组是n-1维数组的数组(n>1)
③链表
为了避免插入和删除的线性开销,我们需要保证表可以不连续存储,否则表的每个部分改变可能会导致整体移动。则链表就可解决这一问题。链表是由一系列节点组成,这些节点不必在内存中相连,每一个节点含有包含表元素和到包含该元素后继元的节点的链,我们称它为next链,最后一个单元的next的引用为null.。只要从表的第一个节点开始然后用一些后继的next链遍历即可。
package dataStructure;
/**
* @author:wh
* @name:单链表的实现
* @date:2017年6月20日 下午2:56:35
* @version: 1.0
* 注意:“=”为值传递,并非引用传递。
*/
public class LinkedList<T> {
private Linked<T> headLinked=null ;
public int nodeSum = 0;
/**
* @param t 插入的元素
*/
public void insert(T t){
Linked<T> node = new Linked();
node.t =t;
if(headLinked==null){
// System.out.println("xx");
headLinked = node;
}else{
Linked<T> head = headLinked;
Linked<T> current = null;
while(head!=null){
current = head;
head = head.next;
}
current.next=node;
}
nodeSum++;
}
/**
* 在第几的位置上插入数字
* @param nodeNum 插入的位置
* @param t 插入的内容
*/
public void insert(int nodeNum,T t){
Linked<T> node = new Linked();
node.t =t;
if(nodeNum<0 ||nodeNum>nodeSum+1){
throw new RuntimeException("插入的位置有误");
}
if(nodeNum==1){
node.next = headLinked;
headLinked = node;
nodeSum++;
}else if(nodeNum ==nodeSum+1 ){
insert(t);
}else{
Linked<T> head = headLinked;
Linked<T> current = null;
while(head!=null&& nodeNum > 1){
current=head;
head = head.next;
nodeNum--;
}
Linked<T> next =current.next;//被插前该位置的元素
node.next = next;
current.next = node;//被插前该位置的父元素
nodeSum++;
}
}
/**
* 在某个位置上删除元素
* @param deletNum
*/
public void delete(int deleteNum){
if(empty()){
throw new RuntimeException("链表已空无法删除");
}
if(deleteNum>nodeSum){
System.out.println("该位置上没有元素,当前最大位置的个数为:"+nodeSum);
}else{
Linked<T> head = headLinked;//
Linked<T> front = new Linked<T>();
front.next = headLinked;
int i = 0;
while(head!=null&&deleteNum>0){
if(i!=0){
front = front.next;//被删除的上一个元素
}
head = head.next;//被删除的下一个元素
i++;
deleteNum--;
}
if(i<=1){//删除第一个
headLinked = headLinked.next;
}else{
front.next = head;
}
nodeSum--;
}
}
public void delete(){
if(empty()){
throw new RuntimeException("链表已空无法删除");
}
Linked<T> head = headLinked;
Linked<T> front = new Linked<T>();
front.next = headLinked;
int i = 0;
while(head!=null){
if(i==1){
front = front.next;//head的上一个节点
}
head = head.next;
i++;
}
if(i<=1){
headLinked = null;
}else{
front.next = null;
}
nodeSum--;
}
@Override
public String toString() {
Linked<T> head = headLinked;
String str = "";
while(head!=null){
str+=head.t.toString();
str+="->";
head = head.next;
}
return str;
}
public boolean empty(){
return headLinked==null?true:false;
}
public static void main(String[] args) {
LinkedList<String> linkList = new LinkedList<String>();
for(int i=0; i<5;i++){
linkList.insert(i+"");
}
linkList.delete(1);
linkList.delete(2);
System.out.println(linkList.toString()+"。个数为:"+linkList.nodeSum);
}
}
class Linked<T>{
public T t;
public Linked<T> next;
}
双链表的实现
package dataStructure;
/**
* @author:wh
* @name:双链表的实现
* @date:2017年6月21日 上午11:04:56
* @version: 1.0
*/
public class DoubleLinked<T> {
private LinkedNode<T> headLinked = null;//链表的头节点,通过头节点可以遍历整个链表
public int nodeSum = 0;//链表中的个数
public boolean empty(){
return headLinked ==null?true:false;
}
public void addNode(T t){
LinkedNode<T> newNode = new LinkedNode<T>(t);
if(empty()){//链表为空
headLinked = newNode;
}else{
LinkedNode<T> head = headLinked;
LinkedNode<T> current =headLinked;
while(head!=null){
current = head;
head = head.next;
}
/*
* 注意:虽然 current.next = head;
* 但是 current.next = newNode 和 head = newNode结果是不一样的
*/
current.next = newNode;//链表最后的一个节点的next为新的节点
newNode.previous = current;
}
nodeSum++;
}
public void addNode(int addNum,T t){
LinkedNode<T> newNode = new LinkedNode<T>(t);
if(addNum>nodeSum+1){
System.out.println("添加的位置已大于链表的长度,当前链表的长度为:"+nodeSum);
}else if(addNum==nodeSum+1){
addNode(t);
}else{
LinkedNode<T> head = headLinked;
LinkedNode<T> current = null;
while(addNum>0){
current =head;
head = head.next;
addNum--;
}
if(current.previous==null){//在第一位添加
newNode.next = headLinked;
headLinked = newNode;
}else{
LinkedNode<T> temp = null;
temp = current.previous ;
temp.next = newNode;
newNode.previous = temp;
newNode.next = current;
current.previous = newNode;
}
nodeSum++;
}
}
public void deleteNode(int deleteNum){
if(empty()){
throw new RuntimeException("链表已空");
}
if(deleteNum>nodeSum+1){
System.out.println("删除的位置已大于链表的长度,当前链表的长度为:"+nodeSum);
}else{
LinkedNode<T> head = headLinked;
LinkedNode<T> current = null;
while(deleteNum>0){
current = head;
head = head.next;
deleteNum--;
}
if(current.previous==null){//当删除的是第一个元素
headLinked = current.next;
}else{
current.previous.next = current.next;
}
nodeSum--;
}
}
@Override
public String toString() {
LinkedNode<T> head = headLinked;
String str="";
while(head!=null){
str += head.t.toString();
head = head.next;
str+="->";
}
return str;
}
public static void main(String[] args) {
DoubleLinked<Integer> doubleLinked =new DoubleLinked<Integer>();
for(int i = 0; i<5;i++){
doubleLinked.addNode(i);
}
doubleLinked.addNode(6,9);
doubleLinked.deleteNode(2);
System.out.println(doubleLinked.toString());
}
}
class LinkedNode<T>{
public T t;
public LinkedNode<T> previous = null;//当前节点的前驱
public LinkedNode<T> next = null;//当前节点的后继
public LinkedNode(T t){
this.t = t;
}
}
双链表的特点是每一个节点有一个指向前驱节点和一个指向后继节点的节点。
④.栈是一种只能在一端进行插入和删除操作的特殊线性表,它按照先进后出的原则存储数据,先进入的数据被压入栈底,后进入的数据在栈顶,需要读数据时,重栈顶开始读。
public class Stack_1<E> {
private Object[]obj=null;
private int maxSize=0;//栈容量
private int top=-1;//栈顶
public Stack_1(){
obj = new Object[10];
maxSize =10;
}
public Stack_1(int initialSize){
if(initialSize<0){
throw new RuntimeException("栈的空间不能小于0"+initialSize);
}else{
obj = new Object[initialSize];
maxSize =initialSize;
}
}
//出栈
public void pop(){
if(top<0){
throw new RuntimeException("栈已空");
}else{
System.out.println("出栈的数据为:"+obj[top]);
top--;
}
}
//入栈
public void push(E e){
if(top >= maxSize-1){
throw new RuntimeException("栈已满");
}else{
top++;
obj[top] = e;
System.out.println("入栈的数据为:"+e);
}
}
//栈的链式存储
public class Stack_2<T> {
private int maxSize;//栈的容量
private int top=0;//栈的当前指针
private Stack<T> headStack=null;
public Stack_2(){
maxSize = 10;
}
public Stack_2(int initialSize){
if(initialSize<0){
throw new RuntimeException("栈的容量不能小于0");
}else{
maxSize = initialSize;
}
}
//入栈:让top指向新创建的元素,新元素的next引用指向原来的栈顶元素
public boolean push(T t){
Stack<T> newStack = new Stack<T>();
newStack.node =t;
if(top>=maxSize){
throw new RuntimeException("栈已满");
}else{
top++;
if(headStack==null){
headStack = newStack;
}else{
newStack.next = headStack;
headStack = newStack;
}
return true;
}
}
public boolean pop(){
if(top<=0){
throw new RuntimeException("栈已空");
}else{
System.out.println("出栈"+headStack.node);
top--;
Stack<T> temp = headStack;//得到栈顶元素
headStack = headStack.next;//得到新的栈顶元素
temp.next = null;//释放老的栈顶元素
return true;
}
}
public String toString(){
Stack<T> temp = headStack;
String str = " ";
while(temp.next!=null){
str +=temp.node+"-->";
temp = temp.next;
}
str +=temp.node;
return str;
}
}
⑤.队列
队列是一种受限制的线性表,它只允许在表头(front)进行删除操作,在表尾(rear)进行插入操作。所以队列的特性是先进先出。
顺序队列:
建立顺序队列结构必须为其静态分配或动态申请一片连续的存储空间,并设置两个指针进行管理。一个是队头指针front,它指向队头元素;另一个是队尾指针rear,它指向下一个入队元素的存储位置。
注意:每次在队尾插入一个元素时rear增1;每次在队头删除一个元素时,front增1。随着插入和删除操作的进行,队列元素的个数不断变化,队列所占的存储空间也在为队列结构所分配的连续空间中移动。当front=rear时,队列中没有任何元素,称为空队列。当rear增加到指向分配的连续空间之外时,队列无法再插入新元素,但这时往往还有大量可用空间未被占用,这些空间是已经出队的队列元素曾经占用过得存储单元。----><<百度百科>>
package dataStructure;
/**
* @author: wh
* @name:数组实现顺序队列
* @date 创建时间:2017年6月19日 上午11:01:41
* @version: 1.0
*/
public class Queue_Order_Array<T> {
private Object[]queue;
private int front =0;
private int rear = 0;
public Queue_Order_Array() {
super();
queue = new Object[5];
}
public Queue_Order_Array(int length) {
super();
queue = new Object[length];
}
public boolean empty_Queue(){
return front==rear?true:false;
}
/*
* 队列满的情况下不能插入:
* rear的值大于定义的数组长度时,队列满了就不能插入。
*
*/
public void in_Queue(T t){
if(rear>=queue.length){
System.out.println("队列已满,"+t.toString()+"无法插入");
}else{
queue[rear] = t;
rear++;
}
}
//当rear和front相等时,队列就空了
public void out_Queue(){
if(!empty_Queue()){
front ++;
}else{
System.out.println("队列已空");
}
}
@Override
public String toString() {
String str ="";
for(int i=front; i<=rear-1;i++){
str+=queue[i].toString();
if(i<rear-1){
str+="->";
}
}
return str;
}
public static void main(String[] args) {
Queue_Order_Array<String> queue = new Queue_Order_Array<String>(10);
queue.in_Queue("h");
queue.in_Queue("e");
queue.in_Queue("l");
queue.in_Queue("l");
queue.in_Queue("o");
queue.in_Queue("h");
queue.in_Queue("e");
queue.in_Queue("l");
queue.in_Queue("l");
queue.in_Queue("o");
queue.in_Queue("h");
System.out.println(queue.toString());
queue.out_Queue();
queue. out_Queue();
queue.out_Queue();
queue. out_Queue();
queue.out_Queue();
queue. out_Queue();
queue.out_Queue();
queue. out_Queue();
System.out.println(queue.toString());
queue. out_Queue();
queue.out_Queue();
queue. out_Queue();
System.out.println(queue.toString());
}
}
package dataStructure;
/**
* @author: wh
* @name:链表实现队列
* @date 创建时间:2017年6月19日 下午1:13:42
* @version: 1.0
* 基于链表的队列,要动态的增加和删除节点,效率底,但是可以动态增长。
*/
public class Queue_Order_linkedList<T> {
private Node<T> front = null;//队头
public Node<T> rear = null;//队尾
public Node<T> bottom = null;//最后进来的数
public boolean empty(){
return front==rear?true:false;
}
public void in_Queue(T t){
Node<T> node = new Node<T>();
node.node = t;
if(front==null){
front =node;
bottom =node;
}else{
bottom.next =node;
bottom =node;
}
rear=bottom.next;
}
public void out_Queue(){
if(empty()){
System.out.println("队列已空");
}else{
front = front.next ;
}
}
@Override
public String toString() {
Node<T> node = front;
String str = "";
while(front.next!=null){
str+=front.node.toString()+" ";
front = front.next;
}
str+=front.node.toString()+" ";
return str;
}
public static void main(String[] args) {
Queue_Order_linkedList<String> link = new Queue_Order_linkedList<String>();
link.in_Queue("h");
link.in_Queue("e");
link.out_Queue();
link.out_Queue();
link.in_Queue("l");
link.in_Queue("l");
link.in_Queue("o");
System.out.println(link.toString());
}
}
class Node<T>{
public T node;
public Node<T> next = null;
}
注意:顺序队列存在两种溢出的情况
第一种:当队列初始化的内存空间全部被使用时,在往队列中插入数据时,从而导致内存溢出的现象。这种情况为真溢出。第二种:当队列初始化的内存空间全部被使用时,从队列中删除部分元素后,在插入数据时,明明存在可用空间(刚刚删除的),但却不能使用。这种情况为假溢出。不过循环队列能很好的解决这一问题。
循环队列:实际使用队列时,为了使队列空间能重复使用,往往对队列的使用方法稍加改进:无论插入或删除,一旦rear指针增1或front指针增1 时超出了所分配的队列空间,就让它指向这片连续空间的起始位置。这实际上是把队列空间想象成一个环形空间,环形空间中的存储单元循环使用,用这种方法管理的队列也就称为循环队列。除了一些简单应用之外,真正实用的队列是循环队列--》《百度百科》
package dataStructure;
/**
* @author: wh
* @name:用数组实现循环队列
* @date 创建时间:2017年6月19日 下午3:03:32
* @version: 1.0
*/
public class Queue_Order_Loop{
private Object []loopQueue;
private int front=0;//队头
private int rear = 0;//对尾
public Queue_Order_Loop() {
loopQueue = new Object[5];
}
/*往队列中插入数据时,只有在队尾在变化,队尾绕了一圈后。当rear==front时,队列已满。
* 其他情况下相等,队列为空,
*/
public void in_Queue(Object obj){
if(rear>loopQueue.length){
rear =rear%loopQueue.length;
if(rear==front){
throw new RuntimeException("队列已满"+obj.toString()+"未能添加成功");
}else{
loopQueue[rear%loopQueue.length] = obj;
rear++;
}
}else{
loopQueue[rear%loopQueue.length] = obj;
rear++;
}
}
//删除情况下,只有队头再变化,当队头绕了一圈或者没绕和队尾相等,队列已空
public void out_Queue(){
if(front%loopQueue.length==rear){
throw new RuntimeException("队列已空");
}else{
loopQueue[front%loopQueue.length]=null;
front++;
}
}
/**
*@parameter
*@return 返回值
*/
@Override
public String toString() {
String str="";
for (int i = 0; i <loopQueue.length; i++) {
str+=loopQueue[i];
if(i<loopQueue.length-1){
str+="->";
}
}
return str;
}
public static void main(String[] args) {
Queue_Order_Loop loop = new Queue_Order_Loop();
for(int i=0; i<5;i++){
loop.in_Queue(i);
if(i==3){
loop.out_Queue();
loop.out_Queue();
loop.out_Queue();
loop.out_Queue();
}
/*loop.out_Queue();
loop.in_Queue(1);
loop.in_Queue(1);*/
System.out.println(loop.toString());
}
}
}
2.数形结构
①树
一棵树是一些节点的集合,这个集合可以是空集,若不是空集,则数由称作根的节点以及0个或多个非空的(子)节点组成,每个节点都有0个或多个(子)节点。
通过以下节点数的声明就可以创建或者遍历一棵树。
class TreeNode{
public Object element;//节点存放的元素
public TreeNode firstChild;//节点的第一个儿子
public TreeNode nextSibling;//节点的第一个儿子的兄弟
}
② 二叉树
二叉树是一颗树,其中每个节点都不能有多余两个儿子。
通过以下节点数的声明就可以创建或者遍历一颗二叉树
class BinaryNode{
public Object element;//节点存放的元素
public BinaryNode left;//节点的第左儿子
public BinaryNode right;//节点的右儿子
}
二叉树遍历
三种遍历方式:
先序遍历:先遍历父节点,在遍历左子节点,最后遍历右子节点 abdefgc
中序遍历:先遍历左子节点,在遍历父节点,最后遍历右子节点, debgfac
后序遍历:先遍历左子节点,在遍历右子节点,最后遍历父节点 edgfbca
遍历技巧:把每个节点当作父节点。
比如以中序遍历为例,先遍历左子节点,b为a的左子节点,但是,b也一个父节点,它也有左子节点,其左子节点为d,d也一个父节点,但是d的左子节点为空,则遍历其父节点d(答案:d),父节点遍历后,在遍历其右子节点(答案 d->e),b的左子节点遍历完后,在遍历b的父节点b(答案:d->e->b),然后遍历右子节点,其右子节点也是个父节点,则在遍历其左子节点g,g也是个父节点,但是没有子节点(答案:d->e->b->g).遍历完g后,在遍历父节点f(答案:d->e->b->g->f),f遍历完后,则b节点就遍历完了,b为a的左子节点,遍历完左子节点后,在遍历父节点a(答案:d->e->b->g->f->a),遍历父节点后,在遍历右子节点c(答案:d->e->b->g->f->a->c)