首页 > 编程语言 > 详细

【数据结构】常用数据结构(Java)

时间:2018-08-22 17:44:05      阅读:152      评论:0      收藏:0      [点我收藏+]

一、线性表:

一个线性表是n个数据元组的有限序列。线性表的常用操作有:创建、销毁、清空、判空、获得长度、获取元素、获取满足条件的特定元素、返回前一个元素、返回后一个元素、插入、删除、遍历。

1.顺序表示:指用一组连续的地址单元存储线性表的元素。

特点是元素在计算机内的存储位置(物理)和线性表数组元素之间的逻辑关系一一对应。通常用数组来描述数据结构的顺序存储结构。

虽然数组创建了之后大小不能再改变了,当数组不能存储线性表中的新元素时,我们可以创建一个新的大数组来替换当前数组。

顺序线性表的优点是可以通过下标来访问或者修改元素,查找比较高效;缺点是插入和删除开销较大,涉及到大量元素的移动。

2.链式表示:用一组任意的存储单元存储线性表的数据元素(可以是连续的,也可以是不连续的)

每个元素叫做节点,节点包含两个部分:和指向下一个节点的指针。将所有节点通过指针链接起来就是一个链表。

技术分享图片

节点:

class Node<E>{
    E value;
    Node<E> next;

    public Node(E value) {
        this.value = value;
        next = null;
    }
}

常用操作:

 //对节点进行初始化
        Node<Integer> head = null;
        Node<Integer> tail = null;
        //创建节点,形成链表结构(头指向尾)
        head = new Node<Integer>(1);
        tail = head;
        //追加节点
        tail.next = new Node<Integer>(2);
        tail = tail.next;//让尾指针重新指向尾部
        //遍历节点
        while (head!=null){
            System.out.println(head.value);
            head=head.next;
        }
    static void printListRev(Node<String> head) {
        //用到递归
        if (head != null) {
            printListRev(head.next);
            System.out.println(head.value);
        }
    }
//单链表反转 主要是逐一改变两个节点间的链接关系来完成 
    static Node<String> revList(Node<String> head) {
        if (head == null) {
            return null;
        }
        Node<String> nodeResult = null;
        Node<String> nodePre = null;
        Node<String> current = head;
        while (current != null) {
            Node<String> nodeNext = current.next;
            if (nodeNext == null) {
                nodeResult = current;
            }
            current.next = nodePre;
            nodePre = current;
            current = nodeNext;
        }
        return nodeResult;
    }

链表还有循环单链表、双向链表、循环双向链表。

循环链表是链表的最后一个节点指向第一个节点,整体构成一个环。

双向链表指节点中不仅有指向下一个节点的指针,还有指向上一个节点的指针。

二、栈和队列

1.栈:限定仅在表尾进行插入或删除操作的线性表。表尾代表栈顶,表头称为栈底。栈是后进先出的线性表。基本操作:创建,销毁,清空,判空,长度,获取栈顶元素,压栈,弹出,遍历。

技术分享图片

迷宫问题:

  1 import java.util.Stack;
  2 
  3 public class maze {
  4     //路径的栈
  5     private Stack<MazeNode> stack = new Stack<MazeNode>();
  6     //迷宫
  7     private int[][] maze = {
  8             {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
  9             {1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1},
 10             {1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1},
 11             {1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1},
 12             {1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1},
 13             {1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1},
 14             {1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1},
 15             {1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1},
 16             {1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1},
 17             {1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1},
 18             {1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1},
 19             {1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1},
 20             {1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1},
 21             {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
 22     };
 23     //参数
 24     private static final int MAZE_SIZE_X = 14;
 25     private static final int MAZE_SIZE_Y = 17;
 26     private static final int END_X = 12;
 27     private static final int END_Y =15;
 28     //检查某一个位置是否走过,用于保存路径
 29     private int[][] mark = new int[MAZE_SIZE_X][MAZE_SIZE_Y];
 30     //初始化mark
 31     private void initMark(){
 32         for (int i =0;i<MAZE_SIZE_X;i++){
 33             for (int j =0;j<MAZE_SIZE_Y;j++){
 34                 mark[i][j]=0;
 35             }
 36         }
 37     }
 38     //迷宫中的点
 39     class Position{
 40         int x;
 41         int y;
 42 
 43         public Position(int x, int y) {
 44             this.x = x;
 45             this.y = y;
 46         }
 47     }
 48     //栈中的节点
 49     class MazeNode{
 50         Position position;
 51         int direction;
 52 
 53         public MazeNode(Position position) {
 54             this.position = position;
 55         }
 56 
 57         public MazeNode(Position position, int direction) {
 58             this.position = position;
 59             this.direction = direction;
 60         }
 61     }
 62 
 63     public void drawMaze(){
 64         //画迷宫,就是遍历迷宫
 65         for (int i =0;i<maze.length;i++){
 66             for (int j=0;j<maze[0].length;j++){
 67                 System.out.println(maze[i][j]);
 68             }
 69             System.out.println("\n");
 70         }
 71         System.out.println("\n");
 72     }
 73 
 74     public void drawResult(){
 75         //画路径,遍历mark数组
 76         initMark();
 77         MazeNode node;
 78         while (!stack.isEmpty()){
 79             node = stack.pop();//stack中存的一条路径,由MazeNode组成,通过该对象获取坐标
 80             mark[node.position.x][node.position.y] =1;
 81         }
 82         for (int i = 0; i < mark.length; i++) {
 83             for (int j = 0; j < mark[0].length; j++) {
 84                 System.out.print(mark[i][j]);
 85             }
 86             System.out.print("\n");
 87         }
 88         System.out.print("\n");
 89     }
 90     //计算下一个位置,顺时针
 91     public Position nextPos(Position position,int direction){
 92         //方向旋转,坐标不变
 93         Position newPosition = new Position(position.x, position.y);
 94         switch (direction) {
 95             case 1:
 96                 newPosition.y += 1;
 97                 break;
 98             case 2:
 99                 newPosition.x += 1;
100                 break;
101             case 3:
102                 newPosition.y -= 1;
103                 break;
104             case 4:
105                 newPosition.x -= 1;
106                 break;
107             default:
108                 break;
109         }
110         return newPosition;
111     }
112 
113     public void process(){
114         initMark();//初始化足迹数组
115         Position curPos = new Position(1,1);//入口
116 
117         do {
118             if (maze[curPos.x][curPos.y]==0&&mark[curPos.x][curPos.y]==0){//当迷宫中该点可走并且未走过
119                 mark[curPos.x][curPos.y]=1;//将该点标识为走过
120                 stack.push(new MazeNode(curPos,1));//将该点压入栈中
121                 //已到终点
122                 if (curPos.x ==END_X&&curPos.y==END_Y){
123                     return;
124                 }
125                 curPos = nextPos(curPos,stack.peek().direction);//获取下一个位置
126 
127             }else {//如果该点不可走或者该点走过
128                 if (!stack.isEmpty()){
129                     MazeNode curNode = stack.pop();//弹出栈顶的元素,回退
130                     while (curNode.direction==4&&!stack.isEmpty()){
131                         //如果四个方向都已经试过并且走不通,那么标记此路线不可行,出栈
132                         mark[curNode.position.x][curNode.position.y]=1;
133                         curNode = stack.pop();//弹出栈顶元素,回退
134                     }
135                     if (curNode.direction<4){
136                         curNode.direction++;//切换方向
137                         stack.push(curNode);//压栈
138                         curPos = nextPos(curNode.position,curNode.direction);//获取下一个位置
139                     }
140                 }
141             }
142         }while (!stack.isEmpty());
143     }
144 
145     public static void main(String[] args) {
146         maze maze = new maze();
147         maze.drawMaze();
148         maze.process();
149         maze.drawResult();
150     }
151 }

2.队列:队列是一种先进先出的线性表,只允许在表的一端进行插入,在另一端删除元素。允许插入的一端叫队尾,允许删除的一端叫队头

技术分享图片

可以使用链表实现队列:

import java.util.LinkedList;

public class Queue<E> {
    private LinkedList<E> list = new LinkedList<>();

    public void enqueue(E e){
        list.addLast(e);
    }

    public E dequeue(){
        return list.removeFirst();
    }
}

三、树

树是n个节点的有限集。一颗非空树,有且仅有一个根节点,当n>1时,其余的节点可以分为多个集合,每个集合本身又是树,称为子树

技术分享图片

树的基本操作:创建、销毁、清空树、判空、深度、返回根节点、返回某个节点的值、给某个节点赋值、返回双亲节点、返回左子树、返回右子树、插入子节点、删除子树、遍历树。

一个节点拥有的子树的个数称为它的,度为0的节点是叶子结点

树中节点的最大层次称为树的深度

1.二叉树

二叉树是每个节点至多有两棵子树,二叉树的子树有左右之分。

满二叉树:深度为k且有2k-1个节点。即除了叶子结点以外其他所有节点度都为2.

完全二叉树:深度为k,有n个节点的二叉树,当每一个编号都与深度为k的满二叉树从1-n的节点对应时,称为完全二叉树。(完全二叉树不一定是满二叉树,这里只是借满二叉树的形式,n个节点的二叉树,从上到下从左到右,编号和节点一一对应就是一个完全二叉树)

1.1 二叉树的性质

  (1)二叉树第i层上至多有2i-1个节点(可以自己举例子,三层的二叉树,第二层最多有两个,第三层最多有四个)

  (2)深度为k的二叉树最多有2k-1个节点(可以这样理解,每一层都是2k个节点除了根节点,所以减一)

  (3)对任意一棵二叉树,如果度为0的节点个数为n0,度为2的节点个数为n2,那么n0=n2+1,即叶子结点的个数比度为2节点个数少一个

  (4)具有n个节点的完全二叉树,它的深度k=└log2n┙+1 (向下取整,≤log2n的最大整数)

  (5)对于一棵有n个节点的完全二叉树:

    如果i=1,该节点为根节点,如果i>1,则它的双亲节点为└i/2┙;

    如果2i>n,则节点i无左孩子,否则其左孩子是2i

    如果2i+1>n,则节点i无有孩子,否则其右孩子为2i+1

2.2 二叉树的存储结构

  (1)顺序存储结构:使用一组连续的地址单元类似数组存储

  (2)链式存储结构:二叉树的节点由数据、指向左子树的指针、指向右子树的指针构成

2.3 遍历二叉树

  (1)先序遍历

    先访问根节点

    再访问左子树

    再访问右子树

  (2)中序遍历

    先访问左子树

    再访问根节点

 

    再访问右子树

  (3)后续遍历

    先访问左子树

    再访问右子树

    再访问根节点

  (4)层序遍历

    从上到下从左到右遍历

  1 import java.util.LinkedList;
  2 import java.util.Queue;
  3 import java.util.Stack;
  4 
  5 class BinaryTreeNode {
  6     private int data;
  7     private BinaryTreeNode left;
  8     private BinaryTreeNode right;
  9 
 10     public BinaryTreeNode(int data, BinaryTreeNode left, BinaryTreeNode right) {
 11         super();
 12         this.data = data;
 13         this.left = left;
 14         this.right = right;
 15     }
 16 
 17     public BinaryTreeNode() {
 18 
 19     }
 20 
 21     public int getData() {
 22         return data;
 23     }
 24 
 25     public void setData(int data) {
 26         this.data = data;
 27     }
 28 
 29     public BinaryTreeNode getLeft() {
 30         return left;
 31     }
 32 
 33     public void setLeft(BinaryTreeNode left) {
 34         this.left = left;
 35     }
 36 
 37     public BinaryTreeNode getRight() {
 38         return right;
 39     }
 40 
 41     public void setRight(BinaryTreeNode right) {
 42         this.right = right;
 43     }
 44 }
 45 
 46 
 47 public class BinaryTree {
 48     /**
 49      * 递归方式前序遍历
 50      */
 51     public void preOrder(BinaryTreeNode root) {
 52         if (root != null) {
 53             System.out.println(root.getData() + "\t");
 54             preOrder(root.getLeft());
 55             preOrder(root.getRight());
 56         }
 57     }
 58 
 59     /**
 60      * 非递归方式前序遍历
 61      */
 62     public void preOrderNonRecursive(BinaryTreeNode root) {
 63         Stack<BinaryTreeNode> stack = new Stack<>();//使用栈保存节点
 64         while (true) {
 65             while (root != null) {
 66                 System.out.println(root.getData());//输出根节点内容
 67                 stack.push(root);//将根节点保存至栈中
 68                 root = root.getLeft();//获取根节点的左子树遍历
 69             }
 70             //左子树全部遍历完毕
 71             if (stack.isEmpty()) break;//代表遍历结束,跳出循环
 72             root = stack.pop();//从栈中获取之前保存的根节点
 73             root = root.getRight();//遍历右子树
 74         }
 75     }
 76 
 77     /**
 78      * 递归方式中序遍历
 79      */
 80     public void inOrder(BinaryTreeNode root) {
 81         if (root != null) {
 82             inOrder(root.getLeft());
 83             System.out.println(root.getData() + "\t");
 84             inOrder(root.getRight());
 85         }
 86     }
 87 
 88     /**
 89      * 非递归方式中序遍历
 90      */
 91     public void inOrderNonRecursive(BinaryTreeNode root) {
 92         Stack<BinaryTreeNode> stack = new Stack<>();//使用栈保存节点
 93         while (true) {
 94             while (root != null) {
 95                 stack.push(root);//将根节点保存至栈中
 96                 root = root.getLeft();//获取根节点的左子树遍历
 97             }
 98             //找到最左下方的叶子节点
 99             if (stack.isEmpty()) break;//代表遍历结束,跳出循环
100             root = stack.pop();//从栈中获取之前保存的根节点
101             System.out.println(root.getData());//输出节点内容
102             root = root.getRight();//遍历右子树
103         }
104     }
105 
106     /**
107      * 递归方式后序遍历
108      */
109     public void postOrder(BinaryTreeNode root) {
110         if (root != null) {
111             postOrder(root.getLeft());
112             postOrder(root.getRight());
113             System.out.println(root.getData() + "\t");
114         }
115     }
116 
117     /**
118      * 非递归方式后序遍历
119      */
120     public void postOrderNonRecursive(BinaryTreeNode root) {
121         Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();//使用栈保存节点
122         while (true) {
123             if (root != null) {
124                 stack.push(root);//将根节点放入栈中
125                 root = root.getLeft();//获取左子树
126             } else {
127                 //当root的右子树是空
128                 if (stack.isEmpty()) return;//如果栈为空说明遍历结束,退出
129                 if (stack.lastElement().getRight() == null) {//从栈顶元素得到右子树,如果右子树为空,则输出该节点的内容,并弹出
130                     root = stack.pop();
131                     System.out.println(root.getData() + "\t");
132                     while (root == stack.lastElement().getRight()) {//如果当前节点是栈顶节点的右子树,就弹出并且输出该节点的内容
133                         System.out.println(stack.lastElement().getData() + "\t");
134                         root = stack.pop();//将根节点从栈中弹出
135                         if (stack.isEmpty()) break;//再次判断栈是否为空
136                     }
137                 }
138                 if (!stack.isEmpty()) {//如果栈非空
139                     root = stack.lastElement().getRight();//从栈顶元素获取右子树
140                 } else {
141                     root = null;
142                 }
143             }
144 
145         }
146     }
147 
148     /**
149      * 层序遍历
150      */
151     public void levelOrder(BinaryTreeNode root) {
152         BinaryTreeNode temp;
153         Queue<BinaryTreeNode> queue = new LinkedList<>();
154         queue.offer(root);
155         while (!queue.isEmpty()) {
156             temp = queue.poll();
157             System.out.println(temp.getData() + "\t");
158             if (temp.getLeft() != null) {
159                 queue.offer(temp.getLeft());
160             }
161             if (temp.getRight() != null) {
162                 queue.offer(temp.getRight());
163             }
164         }
165     }
166 
167     public static void main(String[] args) {
168         BinaryTreeNode node10 = new BinaryTreeNode(10, null, null);
169         BinaryTreeNode node8 = new BinaryTreeNode(8, null, null);
170         BinaryTreeNode node9 = new BinaryTreeNode(9, null, node10);
171         BinaryTreeNode node4 = new BinaryTreeNode(4, null, null);
172         BinaryTreeNode node5 = new BinaryTreeNode(5, node8, node9);
173         BinaryTreeNode node6 = new BinaryTreeNode(6, null, null);
174         BinaryTreeNode node7 = new BinaryTreeNode(7, null, null);
175         BinaryTreeNode node2 = new BinaryTreeNode(2, node4, node5);
176         BinaryTreeNode node3 = new BinaryTreeNode(3, node6, node7);
177         BinaryTreeNode node1 = new BinaryTreeNode(1, node2, node3);
178 
179         BinaryTree tree=new BinaryTree();
180         //采用递归的方式进行遍历
181         System.out.println("-----前序遍历------");
182         tree.preOrder(node1);
183         System.out.println();
184         //采用非递归的方式遍历
185         tree.preOrderNonRecursive(node1);
186         System.out.println();
187 
188 
189         //采用递归的方式进行遍历
190         System.out.println("-----中序遍历------");
191         tree.inOrder(node1);
192         System.out.println();
193         //采用非递归的方式遍历
194         tree.inOrderNonRecursive(node1);
195         System.out.println();
196 
197         //采用递归的方式进行遍历
198         System.out.println("-----后序遍历------");
199         tree.postOrder(node1);
200         System.out.println();
201         //采用非递归的方式遍历
202         tree.postOrderNonRecursive(node1);
203         System.out.println();
204 
205         //采用递归的方式进行遍历
206         System.out.println("-----层序遍历------");
207         tree.levelOrder(node1);
208         System.out.println();
209     }
210 }

技术分享图片

 

 2.4 二叉查找树:也叫二叉搜索树,二叉排序树。它具有以下性质:如果树非空,则

(1)若左子树非空,则左子树上所有结点的值均小于它的根结点的值;

(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;

  (3)   左、右子树也分别为二叉排序树;

  (4)   没有键值相等的结点。

2.5 二叉排序树的查找分析

  对于含有n个节点的二叉排序树的平均查找长度与树的形态有关。当先后插入的关键字有序时,构成的二叉排序树蜕变成单枝树。树深度为n,平均查找长度和n成正比,这是最差的情况,最好的情况是二叉排序树的形态和折半查找的判定树相同,其平均查找长度和log2n成正比。

技术分享图片

为了提高查找效率,通常需要在构成二叉排序树的过程中进行“平衡化”处理,称为平衡二叉树或红黑树,使查找效率为O(logn)。

2.6 平衡二叉树

又称AVL树,它或者是一颗空树,或者具有以下性质:它的左右子树都是平衡二叉树,且左右子树深度之差不超过1

技术分享图片

AVL树是最先发明的自平衡二叉查找树算法。在AVL中任何节点的两个儿子子树的高度最大差别为1,所以它也被称为高度平衡树,n个结点的AVL树最大深度约1.44log2n。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。
2.7 红黑树
是平衡二叉树的一种,它保证最坏情况下查询的时间复杂度为O(logn)。树节点颜色有红、黑两种颜色。且具有以下特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
它具有两种操作,着色和旋转,通过这两种操作保证了红黑树高度的稳定。
 

 参考资料:

https://www.jianshu.com/p/230e6fde9c75

https://www.cnblogs.com/qiuyong/p/6675492.html

http://www.cnblogs.com/skywang12345/p/3245399.html

 

【数据结构】常用数据结构(Java)

原文:https://www.cnblogs.com/JAVALLiuLei/p/9517667.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!