数据结构概述
- 什么是数据?
但凡能够被计算机存储、识别和计算的东西都叫做数据(二进制)
硬盘中:MP3、jpg、doc、avi、exe、txt
内存中:变量、常量、数组、对象、字节码
- 什么是结构?
数据与数据之间的一种或多种特定的关系
- 什么是数据结构?
数据结构=数据+数据之间的关系
- 数据结构主要解决什么样的问题?
这个世界上,零散的数据总比连续的数据多
如何将零散的数据“整齐划一”方便后续的操作?
《离散数学》、《概率论》和《统计学》提供理论基础
数据结构则是具体的实施方案(主要依赖《离散数学》)
- 为什么要这么做?
将看似分散的数据以某种方式联系起来可以从中找到一些规律 --机器学习
这个世界是有一定规律的,如何找到规律,全靠数据 --大数据
- 数据结构的逻辑结构
是指数据元素之间的相互关系,是我们想象出来的,并没有实质性的将其存储在计算机中
- 集合结构:集合结构中的数据元素除了同属于一个集合外,它们之间没有其他关系
- 线性结构:先行结构中的数据元素之间是一对一的关系
- 树形结构:树形结构中的数据元素之间存在一种一对多的层次关系
- 图形结构:图形结构的数据元素是多对多的关系
- 数据结构的物理结构
是指数据的逻辑结构在计算机中的存储形式
- 顺序存储结构:开辟一组连续的空间存储数据
通常用数组来实现,数组中空间本身是连续的,保证了数据之间的关系- 链式存储结构:开辟一组随机的空间存储数据
通常用节点来实现,节点不仅要存储数据,还要存储下一个节点的位置以保证数据之间的关系
算法的概述
- 什么是算法?
是解决特定问题求解步骤的描述。在计算机中表现为指令的有序序列,并且每条指令表示一个或多个操作,说白了,就是求解一个问题的步骤
同一个问题,可以有多种不同的解决方案,也就是说可以用不同的算法去解决同一个问题
- 如何评价一个算法的好坏?
设计算法要提高程序运行的效率,这里效率大都指算法的执行时间
事后统计方法:
这种方法主要是通过设计好的程序和数据,利用计算机计时器对不同算法程序的运行时间进行比较,从而确定算法效率的高低
但是这样会有很多缺陷:
- 必须事先编好程序,再进行运行,如果程序处理的数据量较大,则会花费大量的时间和精力(计算一年都不成问题)
- 时间的比较主要依赖于计算机硬件和软件环境
- 算法的测试数据设计困难,在数据量较小的时候,不管什么算法其运行时间都是很微小的,相差几乎为零。如果数据量大了,算法的优越性就出来了,但是这样又会耗费时间
事后分析估算方法:
这种方法主要在计算机程序编制前,依据统计方法对算法机型估算
一个高级程序语言编写的程序在计算机上运行时所消耗的时间取决于下列因素:
- 算法采用的策略、方法
- 编译产生的代码质量
- 问题的输入规模
- 机器执行指令的速度
第一条当然是算法好坏的根本,第二条要有软件来支持,第四条要看硬件性能,也就是说,抛开这些与计算机硬件、软件有关的因素:
一个程序的运行书简,依赖于算法的好坏和问题的输入规模。
所谓问题输入规模是指输数据入量的多少
- 算法时间复杂度的定义
- 常数阶O(1)
- 线性阶O(n) 忽略常数,忽略N的系数
- 对数阶O(logn)
- 平方阶O(n^2)
动态数组
- Java内置数组的特点
- 数组的长度一旦确定则不可更改
- 数组只能存储同一类型的数据
- 数组中每个存储空间大小一致且地址连续
- 数组提供角标的方式访问元素
- Java内置数组的潜在问题
- 容量不够用时,怎么办?
- 指定位置插入或删除元素,怎么做?
- 数组对象只有length属性,够用不?
- 动态数组的封装
能否用面向对象的思想,将数组进行再度封装呢?
可以的,我们可以把数组的想关属性和相关行为封装在类中
类似字符串String类,形成如下的调用形式
数组对象.排序()
- 如何封装动态数组
属性方面:
int size 数组的有效元素个数
int capacity 数组的最大容量data.length
E[] data 数据的存储容器
行为方面:
增()、删()、改()、查()、其他()
线性表的顺序存储结构
- 线性表的定义
零个或多个数据元素的有限序列
- 线性表接口List的定义
package com.oupeng.线性表;
/**
* List线性表的最终父接口
* @author Administrator
*
* @param <E>
*/
public interface List<E> {
/**
* 获取线性表中元素的个数(线性表的长度)
* @return 线性表中有效元素的个数
*/
public int getSize();
/**
* 判断线性表是否为空
* @return 是否为空的布尔类型值
*/
public boolean isEmpty();
/**
* 在线性表中指定的index角标处添加元素e
* @param index 指定角标0<=index<size
* @param e要插入的元素
*/
public void add(int index,E e);
/**
* 在线性表的表头位置插入一个元素
* @param e要插入的元素 指定在角标size处
*/
public void addFirst(E e);
/**
* 在线性表的表尾位置插入一个元素
* @param e要插入的元素 指定在角标size处
*/
public void addLast(E e);
/**
* 在线性表中获取指定Index角标处的元素
* @param index 指令的角标
* @return 该角标所对应的元素
*/
public E get(int index);
/**
* 获取线性表中表头的元素
* @return 表头元素 index=0
*/
public E getFirst();
/**
* 获取线性表中表尾的元素
* @return 表尾元素 index=size-1
*/
public E getLast();
/**
* 修改线性表中指定Index 处的元素为新元素e
* @param index 指定的角标
* @param e 新元素
*/
public void set(int index,E e);
/**
* 判断线性表中是否包含指定元素e 默认从前往后
* @param e 要判断是否存在的元素
* @return 元素的存在性布尔类型值
*/
public boolean contains(E e);
/**
* 在线性表中获取指定元素e的角标 默认从前往后找
* @param e 查找的元素
* @return 查找到元素的索引
*/
public int find(E e);
/**
* 在线性表中删除指定角标处的元素 并返回
* @param index 指定的角标0<index<size
* @return 删除掉老元素
*/
public E remove(int index);
/**
* 删除线性表中的表头元素
* @return 表头元素
*/
public E removeFirst();
/**
* 删除线性表中的表尾元素
* @return 表尾元素
*/
public E removeList();
/**
* 在线性表中删除指定元素e
* @param e
*/
public void removeElement(E e);
/**
* 清空线性表
*/
public void clear();
}
package com.oupeng.线性表;
/**
* 用顺序存储结构实现List-顺序线性表-顺序表
* @author Administrator
*
* @param <E>
*/
public class ArrayList<E> implements List<E>{
private static int DEFAULT_SIZE=10; //容器的默认容量
private E[] data; //存储元素的容器
private int size; //线性表有效元素的个数
//data.length表示线性表的最大容量Capacity
public ArrayList(){
this(DEFAULT_SIZE);
}
/**
* 创建一个容器为capacity的线性表
* @param capacity
*/
public ArrayList(int capacity){
this.data=(E[]) new Object[capacity];
this.size=0;
}
/**
* 将一个数组封装成一个线性表
* @param arr
*/
public ArrayList(E[] arr){
}
@Override
public int getSize() {
// TODO Auto-generated method stub
return size;
}
@Override
public boolean isEmpty() {
return size==0;
}
@Override
public void add(int index, E e) {
if(index<0||index>size){
throw new ArrayIndexOutOfBoundsException("add函数角标越界");
}
//判断是否已满
if(size==data.length){
resize(2*data.length);
}
for (int i = size-1; i >=index; i--) {
data[i+1]=data[i];
}
data[index]=e;
size++;
}
/**
* 改变data的长度(扩容,缩容)
* @param newlen
*/
private void resize(int newlen){
E[] newData = (E[]) new Object[newlen];
for(int i=0; i<size;i++){
newData[i]=data[i];
}
data=newData;
}
@Override
public void addFirst(E e) {
add(0,e);
}
@Override
public void addLast(E e) {
add(size,e);
}
@Override
public E get(int index) {
if(index<0||index>size-1){
throw new ArrayIndexOutOfBoundsException("get函数角标越界");
}
return data[index];
}
@Override
public E getFirst() {
return get(0);
}
@Override
public E getLast() {
return get(size-1);
}
@Override
public void set(int index, E e) {
if(index<0||index>size-1){
throw new ArrayIndexOutOfBoundsException("set函数角标越界");
}
data[index]=e;
}
@Override
public boolean contains(E e) {
if(isEmpty()){
return false;
}
for(int i=0; i<size;i++){
if(data[i]==e){
return true;
}
}
return false;
}
@Override
public int find(E e) {
if(isEmpty()){
return -1;
}
for (int i = 0; i < size; i++) {
if(data[i]==e){
return i;
}
}
return 0;
}
@Override
public E remove(int index) {
if(index<0||index>size-1){
throw new ArrayIndexOutOfBoundsException("删除角标越界");
}
E e=get(index);
for (int i = index+1; i <=size-1; i++) {
data[i-1]=data[i];
}
size--;
//判断是否缩容
if(data.length>DEFAULT_SIZE&&size<=data.length/4){
resize(data.length/2);
}
return e;
}
@Override
public E removeFirst() {
return remove(0);
}
@Override
public E removeList() {
return remove(size-1);
}
@Override
public void removeElement(E e) {
int index = find(e);
if(index==-1){
throw new IllegalArgumentException("删除元素不存在");
}
remove(index);
}
@Override
public void clear() {
size=0;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("ArrayList:size="+size+",capacity="+data.length+"\n");
if(isEmpty()){
sb.append("[]");
}else{
sb.append("[");
for (int i = 0; i < size; i++) {
sb.append(data[i]);
if(i==size-1){
sb.append(']');
}else{
sb.append(',');
}
}
}
return sb.toString();
}
public int getCapacity(){
return data.length;
}
public void swap(int i,int j){
// i j判断
E temp = data[i];
data[i] = data[j];
data[j] = temp;
}
@Override
public boolean equals(Object obj) {
if(obj==null){
return false;
}
if(obj==this){
return true;
}
if(obj instanceof ArrayList){
ArrayList l = (ArrayList) obj;
if(getSize()==l.getSize()){
for(int i=0;i<getSize();i++){
if(get(i)!=l.get(i)){
return false;
}
}
return true;
}
}
return false;
}
}
Leetcode试题
1
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
class Solution {
public int[] twoSum(int[] nums, int target) {
for(int i = 0; i< nums.length; i++){
for(int j = i+1; j<nums.length; j++){
if(nums[i] == target-nums[j]){
return new int[]{i,j};
}
}
}
return new int[]{-1,-1};
}
}
思路:根据题意,遍历一次数组的基础上,再遍历一次,如果有数组中两个值等于给定值,则返回第一次及第二次遍历的位置,否则,没找到,返回[-1,-1]