1.基本的三种数据结构类型
1、线性表 结点按逻辑关系依次排列形成一个“锁链”
2、树 具有分支、层次特性,其形态有点象自然界中的树
3、图 结点按逻辑关系互相缠绕,任何两个结点都可以邻接
2.数组就是一种典型的线性表
1、数组其实就是一种典型的线性表,而且是一种物理连续的线性表
2、数组的特点:
-
通过下标(内存偏移量,单位是元素个数)进行元素访问
-
数组中每个元素的类型必须一致
-
数组的大小一旦确定就不能变更
3.链表
1、链表是一种逻辑上连续的线性表,所谓逻辑上连续,指的是节点与节点直接无需在内存上物理连续存储,而是通过引用成员来指向下一个节点的位置。
2、链表解决了数组的如下问题:
-
插入,删除的效率非常低
-
数组大小不可变,无法实现动态生成
3、链表和数组的区别:
-
解决了数组无法动态增长及减小的问题
-
插入删除的效率非常高(已经找到了删除或者插入的位置节点)
-
数组的元素寻址访问效率要高于链表
4、链结点元素定义类叫做Node,每个Node对象中都包含一个对下一个链结点引用的字段(通常叫做next)
案例:
package link;
public class MyLinkTest {
public static void main(String[] args) {
MyLink<String> myLink=new MyLink<>();
myLink.add("jack");
myLink.add("jim");
myLink.add("lucy");
myLink.add("jim");
myLink.add("111",0);
myLink.add("222",2);
myLink.add("333",myLink.size());
System.out.println("数量为:"+myLink.size());
myLink.print();
System.out.println("----------删除了");
System.out.println(myLink.remove(0));
System.out.println(myLink.remove(2));
System.out.println( myLink.remove(myLink.size()-1));
System.out.println("剩下了============");
myLink.print();
}
}
package link;
import javax.xml.soap.Node;
/**
* 自定义链表
*/
public class MyLink <T> {
/**
* 存储的数据库数量
*/
private int size;
/**
* 链表头结点 为了方便设置为空
*/
private Node<T> head = new Node<>(null);
/**
* 往尾部添加数据
*
* @param element
*/
public void add(T element) {
// //数据封装为 node对象
// Node<T> newNode = new Node<>(element);
// //添加处理
// if (this.size == 0) {
// this.head.next = newNode;
// } else {
// //查找最后呢一个节点
// Node<T> lastNode = node(size - 1);
// //最后的next指向新节点
// lastNode.next = newNode;
//
// }
// //size加1
// size++;
add(element,size);
}
/**
*
*
*/
public void add(T elemrnt ,int index){
//创建新节点
rangeIndexCheck(index);
Node<T> newNode= new Node<>(elemrnt);
Node<T> preNode=null;
if (index==0){
//index=0的前一个节点是首节点
preNode=this.head;
}else {
//查找index-1位置的节点对象
preNode = node(index - 1);
}
//index位置节点
Node<T> indexNode = preNode.next;
preNode.next = newNode;
newNode.next = indexNode;
//size加1
size++;
}
public T remove(int index){
rangeIndexCheck(index);
Node<T> preNode;
if (index==0) {
//index==0的前一个节点是首节点
preNode=this.head;
}else{
//查找index-1位置的节点对象
preNode = node(index - 1);
}
//index位置节点
Node<T> indexNode=preNode.next;
//preNode的next指向indexNode的next
preNode.next=indexNode.next;
//size减一
size--;
return indexNode.element;
}
private void rangeIndexCheck(int index) {
if (index > size || index < 0) {
//抛出异常
throw new IndexOutOfBoundsException("索引越界,size=" + size + "index=" + index);
}
}
/**
* 返回当前链表数量
*
* @return
*/
public int size() {
return this.size;
}
/**
* 根据索引查找弄得节点对象
*
* @param index
* @return
*/
private Node<T> node(int index) {
//检查index是否越界
rangeIndexCheck(index);
//定义临时节点引用
Node<T> currentNode = this.head;
for (int i = 0; i <= index; i++) {
//移动临时节点 引用指针 ,指向下一个节点
currentNode = currentNode.next;
}
return currentNode;
}
// private void rangeIndexCheck(int index) {
// if (index >= size || index < 0) {
// //抛出异常
// throw new IndexOutOfBoundsException("索引越界,size=" + size + "index=" + index);
// }
// }
/**
* 打印数据
*/
public void print(){
//跳过首节点
Node<T> currentNode=this.head.next;
for (int i = 0; i <size; i++) {
//打印currentNode节点的值
System.out.println(currentNode.element);
//移动currentnode指向下一个节点
currentNode=currentNode.next;
}
}
/**
* 定义链表中的节点类
*
* @param <T>
*/
class Node<T> {
/**
* 存储数据
*/
private T element;
/**
* 存储下一个节点
*/
private Node<T> next;
public Node(T element) {
this.element = element;
}
public T getElement() {
return element;
}
public void setElement(T element) {
this.element = element;
}
public Node<T> getNext() {
return next;
}
public void setNext(Node<T> next) {
this.next = next;
}
}
}
数量为:7
111
jack
222
jim
lucy
jim
333
----------删除了
111
jim
333
剩下了============
jack
222
lucy
jim
4.栈
1、栈只允许访问一个数据项:即最后插入的数据项
2、栈提供了一种“后入先出”的一种数据结构
3、栈是一个线性表(物理或逻辑连续的).有两个标识标志出栈的两个端点–栈底和栈顶
4、栈需要提供2个最基本的操作压入 (push) 和弹出 (pop)
案例 :
package stack;
public class MyStackTest {
public static void main(String[] args) {
MyStack stack=new MyStack(8);
for (int i = 0; i <8 ; i++) {
stack.push(i+1);
}
System.out.println(stack.isFull());
for (int i = 0; i <8; i++) {
System.out.println(stack.pop());
}
System.out.println(stack.isFull());
}
}
package stack;
/**
* 定义栈,栈只存int值
*/
public class MyStack {
//容器存储数据(数组)
private int[] stack;
//压入和弹出时,数组索引
private int top=0;
//栈的大小(创建Mystack对象时指定)
private final int SIZE;
//栈底索引值
private final int BOTTM=0;
public MyStack(int size) {
this.SIZE=size;
stack=new int[size];
}
//判断是否为满
public boolean isFull(){
return top==this.SIZE;
}
//判断是否为空
public boolean isEmpty(){
return top==this.BOTTM;
}
//压入
public void push(int value){
if(isFull()){
throw new IllegalStateException("已经满了不能压入");
}else{
//数组[top]=value top++
stack[top]=value;
top++;
}
}
//弹出
public int pop(){
if(isEmpty()){
throw new IllegalMonitorStateException("已经空了不能弹出");
}else {
top--;
return stack[top];
}
}
}
5.队列
1、队列提供了一种“先入先出”的一种数据结构
2、队列也有两个标识标志出两个端点:队头和队尾
3、队列同样要提供2个最基本的操作入队(offer)和出队(poll)
4、循环队列就是反复的利用同一块存储空间进行队列的移动
5、队列的移动主要依靠两个变量来指示,head end
案例 :
package queue;
import stack.MyStack;
public class MyQueueTest {
public static void main(String[] args) {
MyQueue myQueue=new MyQueue(8);
for (int i = 0; i <7; i++) {
myQueue.off(i+1);
}
for (int i = 0; i <7; i++) {
System.out.println(myQueue.poll());
}
}
}
package queue;
public class MyQueue {
// 队首
private int head;
// 队尾
private int end;
// 存储数据的数据
private int[] queue;
// 容器大小
private final int SIZE;
public MyQueue(int size) {
this.SIZE = size;
queue=new int[size];
}
/**
* 移动索引,用在head,end上
* @param index
* @return
*/
private int next(int index){
return (index+1)%this.SIZE;
}
/**
* 是否满队 往后在走一位
*/
public boolean isFull(){
return next(this.end)==this.head;
}
/**
* 是否空队 是空返回true
*/
public boolean isEmpty() {
return end == head;
}
/**
* 入队
*/
public void off(int data) {
if (isFull()) {
throw new IllegalStateException("满了");
}
//存储输入
queue[end] = data;
//队尾后移
end = next(end);
}
/**
* 出队
*/
public int poll() {
if (isEmpty()) {
throw new IllegalStateException("空了");
}
int result = queue[head];
//队首后移
head = next(head);
return result;
}
}
6.哈希表
1、存放的数据往往包含两个部分:关键字(Key)作为存储和检索的索引,数据(Data)存放实际的数据项
2、在Hash表中,理想状态下记录在表中的位置和其关键字之间存在着一种确定的关系。这样就能预先知道所查关键字在表中的位置,从而直接通过下标找到记录
3、表中元素和关键字之间的关系由哈希函数确定,即hash(key)=位置
4、由于哈希函数是一个压缩映象,因此,在一般情况下,很容易产生“冲突”现象,即:key1≠key2,而hash(key1)=hash(key2)
5、常见Hash构造函数的方法:
-
直接定址法
-
数字分析法
-
平方取中法
-
折叠法
-
除留余数法
-
随机数法
6、“处理冲突”的实际含义是:为产生冲突的关键字寻找下一个哈希地址,包括开放定址法、再哈希法、链地址法等,链地址法被很多语言的Hash表默认实现所选择
7、装载因子就是hash表中已经存储的关键字个数,与可以散列位置的比值,表征着hash表中的拥挤情况,一般而言,该值越大则越容易发生冲突