一、什么叫数据结构?
程序 = 数据结构 + 算法
1、数据的逻辑结构
指反映数据元素之间的逻辑关系的数据结构,其中的逻辑关系是指数据元素之间的前后间关系,而与他们在计算机中的存储位置无关。逻辑结构包括:
1.集合:数据结构中的元素之间除了“同属一个集合” 的相互关系外,别无其他关系;
2.线性结构:数据结构中的元素存在一对一的相互关系;(表)
3.树形结构:数据结构中的元素存在一对多的相互关系;(倒悬树)
4.图形结构:数据结构中的元素存在多对多的相互关系。(地图)
2、数据的物理(内存)结构
指数据的逻辑在计算机存储空间的存放形式。
1.顺序结构(数组)
数组存储在连续的内存空间中,用数据元素的相对位置来表示关系。
2.链式结构(链表)
数据元素存储在彼此独立的内存空间中,每个独立的元素也叫做节点,每个数据元素中增加一个数据项用来存储其他元素的地址,用来表示元素之间的关系。
逻辑结构与物理结构的关系
表 --> 顺序 、链式
树 --> 顺序 、链式
图 --> 顺序 + 链式
A.顺序结构实现
- 底层实现:
数组 : Array ,是有序的元素序列,数组是在内存中开辟⼀段连续的空间,并在此空间存放元素。
- 特点:1.按照顺序排列,每个元素都带有标号;2.除了有标号是连续的,内存中的物理空间也是连续的。
- 优缺点:查询快,增删慢。
package day16_17data.Arr;
import java.util.Arrays;
/*
数据结构分为逻辑结构 和 存储(物理)结构
逻辑结构:
集合: 数据元素同属于一个集体,但是元素之间没有任何关系
线性结构: 数据元素之间存在一对一的关系(表)
树型结构: 数据元素之间存在一对多的关系(倒悬树)
图型结构: 数据元素之间存在多对多的关系(地图)
物理(内存)结构:
顺序结构(数组)
数组存储在连续的内存空间中,用数据元素的相对位置来表示关系.
链式结构(链表)
数据元素存储在彼此独立的内存空间中,每个独立的元素也叫做节点,
每个数据元素中增加一个数据项用来存储其他元素的地址,
用来表示元素之间的关系.
逻辑结构和物理结构之间的关系:
表 -> 顺序 链式
树 -> 顺序 链式
图 -> 顺序 + 链式
------------------------------------------------------------------------------
顺序结构:
底层实现:数组
目的: 为了让外部类看起来是一个可变长的数组
1.向集合(this)中末尾添加元素
2.向集合index的位置中插入obj元素
3.删除指定位置(index)上的元素,并且返回删除的元素
4.删除第一个指定元素(obj)
5.替换指定位置上的元素,替换成obj,并且返回被替换的元素
6.从集合中获得指定位置(index)的元素
7.获得集合中的元素个数
8.判断集合中是否存在指定元素obj
9.判断集合是否为空:没有有效元素是空
10.打印出在集合中的有效元素
*/
public class ArraysDemo01 {
//成员变量
Object[] arr = new Object[5];
int size = 0;//表示有效元素个数
//1.向集合(this)中末尾添加元素
public void add(Object obj){
//判断有效个数是否超过数组的长度
if(size == arr.length){
//数组扩容
arr = Arrays.copyOf(arr,arr.length + 5);
}
//arr[arr.length-1] = obj;//添加到末尾
//将元素添加到数组中
arr[size] = obj;
//数组个数+1
size++;
}
//2.向集合index的位置中插入obj元素
public void add(int index,Object obj){
//解决越界问题
if(index < 0 || index > size - 1){
return;
}
//数组扩容
if(size == arr.length){
arr = Arrays.copyOf(arr,arr.length + 5);
}
for (int i = size - 1 ; i >= index ; i--) {
arr[i] = arr[i-1];
}
arr[index] = obj;
size++;
}
//3.删除指定位置(index)上的元素,并且返回删除的元素
public Object remove(int index){
//解决越界问题
if(index > size - 1 || index < 0){
return null;
}
//将被删除的元素保存下来
Object o = arr[index];
for (int i = index + 1; i < size; i++) {
arr[i - 1] = arr[i];
}
size--;
return o;
}
//4.删除第一个指定元素(obj)
public void remove(Object obj){
for (int i = 0; i < size; i++) {
if (obj.equals(arr[i])){
//如果相同
remove(i);
break;
}
}
}
//5.替换指定位置上的元素,替换成obj,并且返回被替换的元素
public Object set(int index,Object obj){
//解决越界问题
if(index > size - 1 || index < 0){
return null;
}
//保存index位置上的元素
Object o = arr[index];
//替换元素
arr[index] = obj;
return o;
}
//6.从集合中获得指定位置(index)的元素
public Object get(int index){
return arr[index];
}
// 7.获得集合中的元素个数 -> size
public int size(){
return size;
}
// 8.判断集合中是否存在指定元素obj
public boolean contains(Object obj){
for (int i = 0; i < size; i++) {
if(obj.equals(arr[i])){
return true;
}
}
return false;
}
//9.判断集合是否为空:没有有效元素是空
public boolean isEmpty(){
return size == 0;
}
//10.打印出在集合中的有效元素
public String toString(){
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < size; i++) {
sb.append(arr[i] + ",");
}
//去除最后一位
//sb.deleteCharAt(sb.length()-1);
//sb.append("]");
sb.replace(sb.length()-1,sb.length(),"]");
//字符串的拼接
return sb.toString();
}
}
public class MainArr01 {
public static void main(String[] args) {
ArraysDemo01 list = new ArraysDemo01();
list.add(25);
list.add(8);
System.out.println(list);
list.add(1,"hi");//指定位置+
System.out.println(list);
list.remove(0);
list.contains("hi");
System.out.println(list.toString());//[hi,null]
}
}
B.链式结构实现
- 底层实现:节点(data数据 + next下一个节点的引用)
linked list ,由⼀系列结点 node (链表中每⼀个元素称为结点)组成,结点可以在运⾏时i 动态⽣成。每个结点包括两个部分:⼀个是存储数据元素的数据域,另⼀个是存储下⼀个结点地址的指针域。
- 特点:1.链表是内存中固定顺序,但是他的物理空间不连续
2.没有下标,并且从头节点开始
3.所有节点的访问,都必须通过头节点
4.head(头节点): 只存next,不存data
5.head.next = null -> 空链表 - 三种类型的链表:1.单向链表:只有头节点,只能从头节点出发;2.双向链表:有头节点和尾结点,可以从头或者尾出发;3.循环链表:尾节点指向头节点,可以从任意位置开始。
- 优缺点:查询慢,插入、删除效率高;
public class SingleLinkDemo {
//链表中只存在节点
private Node head = new Node();//初始化Node
public int size = 0;//表示有效元素个数
//成员内部类
public class Node{
public Object data;//存储数据
public Node next;//下一个节点的引用
}
//获取index之前的一个节点
public Node prevNode(int index){
/*if (index > size - 1 || index < 0){
return null;
}*/
//获取头节点
Node node = head;
//找到index下标前的一个节点位置
for (int i = 0; i < index; i++) {
node = node.next;
}
return node;
}
//1.向集合(this)中末尾添加元素
public void add(Object obj){
//获取头节点
Node node = head;
//判断下一个节点是否为空
while(node.next != null){
//将节点往下移动一位
node = node.next;
}
//新建节点
Node newNode = new Node();
//存储新节点的数据
newNode.data = obj;
//上一个节点指向新的节点
node.next = newNode;
//节点个数+1
size++;
}
//2.向集合index的位置中插入obj元素
public void add(int index,Object obj){
//获取头节点
Node node = head;
//找到index下标前的一个节点位置
for (int i = 0; i < index; i++) {
node = node.next;
}
//Node node = prevNode(index);
//插入新的节点
Node newNode = new Node();
newNode.data = obj;
//插入新节点:
//a.新节点的下一个节点是原来的节点
newNode.next = node.next;
//b.node的下一个节点等于新的节点
node.next = newNode;
//元素个数+1
size++;
}
//3.删除指定位置(index)上的元素,并且返回删除的元素
public Object remove(int index){
//找到index之前的一个节点
Node node = prevNode(index);
//保存index位置上的数据
Object o = node.next.data;
node.next = node.next.next;
size--;
return o;
}
// 4.删除第一个指定元素(obj)
public void remove(Object obj){
Node node = head;
//假设下标
int index = -1;
//方式一:
while(node.next != null){
index++;
node = node.next;
//判断node节点上的数据和 obj是否相同
if(node.data.equals(obj)){
remove(index);
break;
}
}
//方式二:
/*while(node.next.data != obj){
node = node.next;
}
node.next = node.next.next;*/
//方式三:
/*for (;node.next.data != obj; index++) {
node = node.next;
}
remove(index);*/
//方式四:
/*for (int i = 0; i < size; i++) {
if(node.next.data != obj){
node = node.next;
}
}
size--;*/
}
//5.替换指定位置上的元素,替换成obj,并且返回被替换的元素
public Object set(int index,Object obj){
Node node = prevNode(index + 1);
Object o = node.data;
node.data = obj;
return o;
}
// 6.从集合中获得指定位置(index)的元素
public Object get(int index){
//获取index当前节点位置
Node node = prevNode(index + 1);
return node.data;
}
// 7.获得集合中的元素个数
public int size(){
//会产生栈溢出
//return size();
return size;
}
// 8.判断集合中是否存在指定元素obj
public boolean contains(Object obj){
Node node = head;
while (node.next != null){
node = node.next;
if(node.data.equals(obj)){
return true;
}
}
return false;
}
// 9.判断集合是否为空:没有有效元素是空
public boolean isEmpty(){
return size == 0;
}
public String toString(){
StringBuilder sb = new StringBuilder("[");
//获取链表中的元素
Node node = head;
while (node.next != null){
node = node.next;
sb.append(node.data + ",");
}
return sb.toString();
}
}
public class MainLink {
public static void main(String[] args) {
SingleLinkDemo slink = new SingleLinkDemo();
slink.add(1);
slink.add(2);
slink.add(3);
slink.add(4);
slink.add(2,10);
System.out.println(slink);
slink.isEmpty();
slink.get(1);
slink.contains(2);
slink.remove(0);
}
}
二、常见的数据结构
1、栈(Stack)
特点:
-
先进后出(即,存进去的元素,要在后它后⾯的元素依次取出后,才能取出该元素)。
-
栈的⼊口、出⼝的都是栈的顶端位置。
自定义代码实现栈 (数组)
/*
栈
用数组实现
*/
public class ArraysDemo01 <T>{
private Object[] data;//存储数据的数组
private int size = 0;//表示元素有效个数
ArraysDemo01(){
data = new Object[10];//初始化数组长度
}
ArraysDemo01(int cap){
data = new Object[cap];//初始化数组长度
}
//压栈
public void push(T t){
//数组扩容
if(size == data.length){
data = Arrays.copyOf(data,data.length + 5);
}
/* 将obj元素添加到数组中
data[size] = obj;
//有效个数+1
size++;*/
//等同于以上的写法
data[size++] = t;
}
//弹栈
public T pop(){
//有效个数的下标 size-1
/*T t = (T)data[size - 1];
size--;*/
return (T)data[--size];//弹出去一个 -1
}
//打印出在集合中的有效元素
public String toString(){
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < size; i++) {
sb.append(data[i] + ",");
}
//去除最后一位
//sb.deleteCharAt(sb.length()-1);
//sb.append("]");
sb.replace(sb.length()-1,sb.length(),"]");
//字符串的拼接
return sb.toString();
}
}
//数组实现栈测试
public class Main01 {
public static void main(String[] args) {
ArraysDemo01 arrStack = new ArraysDemo01();
arrStack.push(22);
arrStack.push("xixi");
System.out.println(arrStack);//[22,xixi]
arrStack.pop();
System.out.println(arrStack);//[22]
}
}
自定义代码实现栈 (链表)
/*
链表方式实现栈
*/
public class LinkDemo02 {
//头节点
private Node head = new Node();
//节点类
public class Node{
public Object data;
public Node next;
}
//压栈
public void push(Object obj){
//创建新的节点
Node newNode = new Node();
newNode.data = obj;
while (head.next != null){
newNode.next = head.next;
}
//让头节点的next指向新加入的节点
head.next = newNode;
}
//弹栈
public Object pop(){
//保存被弹出的数据
Node node = head.next;
//让头节点指向下下节点
head.next = head.next.next;
//获取被弹出的节点的数据
return node.data;
}
}
//链表实现栈测试
public class Main02 {
public static void main(String[] args) {
LinkDemo02 linkStack = new LinkDemo02();
linkStack.push("xii");
linkStack.push(5555);
System.out.println(linkStack);
linkStack.pop();
System.out.println(linkStack);
}
}
2.队列(Queue)
-
先进先出(即,存进去的元素,要在后它前⾯的元素依次取出后,才能取出该元素)。
-
队列的⼊⼝、出⼝各占⼀侧。
自定义代码实现队列(链表)也可以通过数组实现
/*
链表实现队列
*/
public class LinkQueueDemo {
//单链表作为成员变量使用
private SingleLinkDemo link = new SingleLinkDemo();
//入队:
public void push(Object obj){
link.add(0,obj);
}
//出列:
public Object pop(){
//获取元素
Object o = link.get(link.size() - 1);
//删除元素
link.remove(link.size() - 1);
return o;
}
@Override
public String toString() {
return link.toString();
}
}
public class MainQueue {
public static void main(String[] args) {
LinkQueueDemo q = new LinkQueueDemo();
q.push(55);
q.push("55555");;
System.out.println(q);
q.pop();
System.out.println(q);
}
}
数组跟链表看上面顺序结构跟链式结构的解释。
3.二叉树:红黑树
三、分类
数据结构有很多种,一般来说,按照数据的逻辑结构对其进行简单的分类,包括线性结构和非线性结构两类。
1.线性结构
简单地说,线性结构就是表中各个结点具有线性关系。如果从数据结构的语言来描述,线性结构应该包括如下几点:
1、线性结构是非空集。
2、线性结构有且仅有一个开始结点和一个终端结点。
3、线性结构所有结点都最多只有一个直接前趋结点和一个直接后继结点。
线性表就是典型的线性结构,还有栈、队列和串等都属于线性结构。
2.非线性结构
简单地说,非线性结构就是表中各个结点之间具有多个对应关系。如果从数据结构的语言来描述,非线性结构应该包括如下几点:
1、非线性结构是非空集。
2、非线性结构的一个结点可能有多个直接前趋结点和多个直接后继结点。
在实际应用中,数组、广义表、树结构和图结构等数据结构都属于非线性结构。