什么是数据结构:
数据结构是计算机存储、组织数据的方式。
数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。
通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关。
常见的数据结构
数组,栈,链表,哈希表,队列,堆,图,树
Java中集合框架其实就是数据结构的实现的封装,今天是我们自己从头来模拟和实现数据结构.
不同的数据结构的操作性能是不同的:(有的查询性能很快,有的插入速度很快,有的是插入头和尾速度很快,有的做等值判断很快,有的做范围查找很快,有的允许元素重复,有的不允许重复等等),在开发中如何选择,要根据具体的需求来选择.
最简单的数据结构就是数组.
什么是集合框架:
集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。任何集合框架都包含三大块内容:对外的接口、接口的实现和对集合运算的算法(底层都对应着某一种数据结构的算法)。
--------------------------------------------------------------------------------------------------------------------------------
为什么需要集合框架(把集合框架的类和接口都存放在java.util包中):
1):提供功能的复用(java.util包).
2):让程序猿专注于业务开发,而不是数据结构和算法.
--------------------------------------------------------------------------------------------------------------------------------
常用的框架接口规范:
集合中存储的对象,称之为集合元素.
常用的集合类:
Set(集):集合中的对象不按特定方式排序,不允许元素重复.
List(列表):集合中的对象按照索引位置排序,允许元素重复.
Map(映射):集合中每一个元素都包含一对key和value对象.不允许key对象重复,值对象可以重复.
ArrayList操作的性能分析
发现:基于数组的结构做查询是和修改是非常快的,但是做保存和删除操作比较慢了.
在Java7之前,即使使用new ArrayList创建对象,一个元素都不存储,但是在堆空间依然初始化了长度位10的Object数组,没必要.
从Java7开始优化这个设计,new ArrayList,其实底层创建的使用一个空数组.
Object [] elementData = new Object[]{};
在第一次调用add方法的时候,才会重新去初始化数组.
链表:
链表结构(火车和火车车厢):
1):单向链表:只能从头遍历到尾/只能从尾遍历到头.
2):双向链表:既可以从头遍历到尾,又可以从尾遍历到头.
-------------------------------------
通过引用来表示上一个节点和下一个节点的关系
需要在Node中添加一个构造方法
给当前ele进行赋值
public Node(Object ele){
this.ele = ele;
}
重写toString方便打印
LinkedList基于双向链表
初始化时, 有个header entry 值都null
使用header的优点:在任何一个条目(包括第一个和最后一个),都可以有一个前置条目
或一个后置条目,插入都会比较快,可以直接在开头和末尾添加元素
ArrayList:查询,更改较快,新增和删除较慢.
LinkedList:查询,更改较慢,新增和删除较快.
Hash,一般翻译做“散列”,也有直接音译为“哈希”的,它是基于快速存取的角度设计的,也是一种典型的“空间换时间”的做法。顾名思义,该数据结构可以理解为一个线性表,但是其中的元素不是紧密排列的,而是可能存在空隙。
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。比如我们存储70个元素,但我们可能为这70个元素申请了100个元素的空间。70/100=0.7,这个数字称为负载(加载)因子。我们之所以这样做,也 是为了“快速存取”的目的。我们基于一种结果尽可能随机平均分布的固定函数H为每个元素安排存储位置,这样就可以避免遍历性质的线性搜索,以达到快速存取。但是由于此随机性,也必然导致一个问题就是冲突。所谓冲突,即两个元素通过散列函数H得到的地址相同,那么这两个元素称为“同义词”。这类似于70个人去一个有100个椅子的饭店吃饭。散列函数的计算结果是一个存储单位地址,每个存储单位称为“桶”。设一个散列表有m个桶,则散列函数的值域应为[0,m-1]。
这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)%len获得,也就是元素的key的哈希值对数组长度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存储在数组下标为12的位置
可是当哈希表接近装满时,因为数组的扩容问题,性能较低(转移到更大的哈希表中).
Java默认的散列单元大小全部都是2的幂,初始值为16(2的4次幂)。假如16条链表中的75%链接有数据的时候,则认为加载因子达到默认的0.75。HahSet开始重新散列,也就是将原来的散列结构全部抛弃,重新开辟一个散列单元大小为32(2的5次幂)的散列结果,并重新计算各个数据的存储位置。以此类推下去.....
负载(加载)因子:0.75.-->hash表提供的空间是16 也就是说当到达12的时候就扩容
排重机制
假如我们有一个数据(散列码76268),而此时的HashSet有128个散列单元,那么这个数据将有可能插入到数组的第108个链表中(76268%128=108)。但这只是有可能,如果在第108号链表中发现有一个老数据与新数据equals()=true的话,这个新数据将被视为已经加入,而不再重复丢入链表。
直接根据数据的散列码和散列表的数组大小计算除余后,就得到了所在数组的位置,然后再查找链表中是否有这个数据即可。查找的代价也就是在链表中,但是真正一条链表中的数据很少,有的甚至没有。几乎没有什么迭代的代价可言了。所以散列表的查找效率建立在散列单元所指向的链表中的数据要少 。
哈希表的插入和查找是很优秀的.
Map集合
映射的数学解释:
设A、B是两个非空集合,如果存在一个法则f,使得对A中的每个元素a,按法则f,在B中有唯一确定的元素b与之对应,则称f为从A到B的映射,记作f:A→B。
-------------------------------------------------------------
映射关系(两个集合):A集合和B集合.
A集合中的每一个元素都可以在B集合中找到唯一的一个值与之对应.
-------------------------------------------------------------
严格上说,Map并不是集合,而是两个集合之间的映射关系(Map接口并没有继承于Collection接口),然而因为Map可以存储数据(每次存储都应该存储A集合中以一个元素(key),B集合中一个元素(value)),我们还是习惯把Map也称之为集合.
-------------------------------------------------------------
因为:Map接口并没有继承于Collection接口也没有继承于Iterable接口,所以不能直接对Map使用for-each操作.
ps:1.key(键)必须唯一
2.value(值)可以不唯一
例如: key1 = value1
key2 = value1
如下不行:
key1 = value1
key1 = value2
斗地主发牌
|
/** * 扑克牌类 * @author JKMaster * */ public class Poker { //表示存储的花色和点数 private static final String[] color = {"♥","♦","♠","♣"}; private static final String[] num = {"2","3","4","5","6","7","8","9","10","J","Q","K","A"}; private static final String[] king = {"大�","小�"};
//存储所有的牌 private static List<String> list = new ArrayList<>();
//存储牌 public static void setPoker() { for(int i =0;i<color.length;i++) {//花色 for(int j = 0;j<num.length;j++) {//点数 list.add(color[i]+num[j]);
} if(i<king.length) { list.add(king[i]); } } }
//洗牌 public static void flushPoker() { Collections.shuffle(list); }
//发牌 public static void dealPoker(List<String> p1List,List<String> p2List,List<String> p3List,List<String> p4List) { //循环遍历操作集合中的元素下标,操作了集合集合中的元素 for(int i = 0;i<list.size();i++) { if(i<list.size()-3) { if (i % 3 == 0) { p1List.add(list.get(i)); } else if (i % 2 == 0) { p2List.add(list.get(i)); } else { p3List.add(list.get(i)); } }else { p4List.add(list.get(i));//最后留给地主的3牌 } } } public class Player { private String name; // 存储玩家手里的牌 private List<String> pokers = new ArrayList<>();
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public List<String> getPokers() { return pokers; }
public void setPokers(List<String> pokers) { this.pokers = pokers; }
public Player() { super(); // TODO Auto-generated constructor stub }
public Player(String name) { super(); this.name = name; this.pokers = pokers; }
//打印玩家手里的牌 public void showInfosPoker() { System.out.println(name+":"+pokers); }
}
public class Relu implements Comparator<String> {
@Override public int compare(String o1, String o2) { //定义两个Integer类型存储 转换后的值进行比较 Integer i1; Integer i2; //点数和花色进行拆分 //"2","3","4","5","6","7","8","9","10","J","Q","K","A" "�","�" String str1 = o1.substring(1); String str2 = o2.substring(1);
if(str1.equals("J")) { i1 = 11; }else if(str1.equals("Q")) { i1 = 12; }else if(str1.equals("K")) { i1 = 13; }else if(str1.equals("A")) { i1 = 14; }else if(str1.equals("�")) { i1 = 15; }else if(str1.equals("�")) { i1 = 16; }else { i1 = new Integer(str1); }
if(str2.equals("J")) { i2 = 11; }else if(str2.equals("Q")) { i2 = 12; }else if(str2.equals("K")) { i2 = 13; }else if(str2.equals("A")) { i2 = 14; }else if(str2.equals("�")) { i2 = 15; }else if(str2.equals("�")) { i2 = 16; }else { i2 = new Integer(str2); } return i1 - i2; }
} public class Test { public static void main(String[] args) throws InterruptedException { System.out.println("------------------------------------JJ斗地主----------------------------------------"); System.out.println("正在洗牌....."); Poker.setPoker(); Poker.flushPoker(); Thread.sleep(1000); //让当前线程睡眠,卡顿效果 毫秒单位
Scanner input = new Scanner(System.in); System.out.println("请输入三个玩家的姓名:"); String name1 = input.next(); String name2 = input.next(); String name3 = input.next();
//存储3个玩家手里的扑克牌 List<String> p1List = new ArrayList<>(); List<String> p2List = new ArrayList<>(); List<String> p3List = new ArrayList<>(); //存储剩余的3张牌 List<String> p4List = new ArrayList<>();
System.out.println("进入发牌阶段......"); Poker.dealPoker(p1List, p2List, p3List, p4List); Thread.sleep(1000); //������� --> ���ٵ�Ч��
//玩家手里牌进行排序 Collections.sort(p1List,new Relu()); Collections.sort(p2List,new Relu()); Collections.sort(p3List,new Relu());
//将牌设置给玩家 Player p1 = new Player(name1); Player p2 = new Player(name2); Player p3 = new Player(name3); p1.setPokers(p1List); p2.setPokers(p2List); p3.setPokers(p3List);
System.out.println("展示玩家的手牌:"); p1.showInfosPoker(); p2.showInfosPoker(); p3.showInfosPoker();
System.out.println("请输入要叫地主玩家的姓名:"); String name4 = input.next(); System.out.println("底牌是:"+p4List); if(p1.getName().equals(name4)) { p1List.addAll(p4List); Collections.sort(p1List,new Relu()); }else if(p2.getName().equals(name4)) { p2List.addAll(p4List); Collections.sort(p1List,new Relu()); }else { p3List.addAll(p4List); Collections.sort(p1List,new Relu()); }
System.out.println("展示玩家的手牌:"); p1.showInfosPoker(); p2.showInfosPoker(); p3.showInfosPoker(); |