2数据结构基础

二数据结构基础

物理结构:顺序存储结构(数组);链式存储结构(链表)

逻辑结构:线性结构(顺序表,栈,队列);非线性结构(树,图)

数组(顺序表):由有限个相同类型的变量所组成的有序集合,在内存中顺序存储

1读取元素

int[] array= new int[]{3,1,2,5,4,9,7,6};
System.out.println(arrray[3]);

2更新元素

int[] array =new int[]{3,1,2,5,4,9,7,6};
array[5]=10;
System.out.println(arrray[5]);

3.1中间插入

 public void insert(int element, int index) throws Exception{
        if (index<0||index>size){
            throw new IndexOutOfBoundsException("超出实际元素范围");
        }
        for (int i=size-1;i>=index;i--){
            array[i+1]=array[index];
        }
        array[index]=element;
        size++;
    }

3.2越界插入

 public void insert(int element, int index) throws Exception{
        if (index<0||index>size){
            throw new IndexOutOfBoundsException("超出实际元素范围");
        }
        if (size>=array.length){
            resize();
        }
        for (int i=size-1;i>=index;i--){
            array[i+1]=array[index];
        }
        array[index]=element;
        size++;
    }
    
    //数组扩容
    public void resize(){
        int[] arrayNew=new int[array.length*2];
        System.arraycopy(array,0,arrayNew,array.length);
        array=arrayNew;
    }

4删除元素

 public int delete(int index) throws Exception{
        if (index<0||index>size){
            throw new IndexOutOfBoundsException("超出实际元素范围");
        }
        int deletedElement=array[index];
        
        for (int i=index;i<size-1;i++){
            array[i]=array[i+1];
        }
        size--;
        return deletedElement;
    }

数组的插入和删除操作时间复杂度都为O(n)

数组优势:非常高效的随机访问能力,只要给出下标就可以用常量时间找到对应元素。(二分查找)

数组劣势:插入删除会导致大量元素被迫移动,影响效率

总结:数组适合读操作多写操作少的场景

链表:一种在物理上非连续,非顺序的数据结构,由若干节点组成,随机存储

单向链表:包含两部一部分存放数据的变量,另一部分指向下一个节点的指针

private static class Node{
	int data;
	Node next;
}

双向链表:每一个节点除了拥有data和next指针以外,还有指向前置节点的prev指针

1链表操作的完整代码

 private ListNode head;
    private ListNode last;
    private int size;

    //链表插入元素
    public void insert(int data,int index) throws Exception{
        if (index<0||index>size){
            throw new IndexOutOfBoundsException("超出链表节点范围");
        }
        ListNode insertedNode=new ListNode(data);
        if (size==0){
            //插入头部
            head=insertedNode;
            last=insertedNode;
        }else if(size==index){
            //插入尾部
            last.nextNode=insertedNode;
            last=insertedNode;
        }else {
            //插入中间
            ListNode prevNode=get(index-1);
            ListNode nextNode=prevNode.nextNode;
            prevNode.nextNode=insertedNode;
            insertedNode.nextNode=nextNode;
        }
        size++;
    }

	//链表删除元素
    public ListNode remove(int index) throws Exception{
        if (index<0||index>=size){
            throw new IndexOutOfBoundsException("超出链表节点范围");
        }
        ListNode removedNode=null;
        if (index==0){
            //删除头结点
            removedNode=head;
            head=head.nextNode;
        }else if (index==size-1){
            //删除尾节点
            ListNode prevNode=get(index-1);
            removedNode=prevNode.nextNode;
            prevNode.nextNode=null;
            last=prevNode;
        }else{
            //删除中间节点
            ListNode prevNode=get(index-1);
            ListNode nextNode=prevNode.nextNode.nextNode;
            removedNode=prevNode.nextNode;
            prevNode.nextNode=nextNode;
        }
        size--;
        return removedNode;
    }

    //链表查找元素
    public ListNode get(int index)throws Exception{
        if (index<0||index>=size){
            throw new IndexOutOfBoundsException("超出链表节点范围");
        }
        ListNode temp=head;
        for (int i=0;i<index;i++){
            temp=temp.nextNode;
        }
        size--;
        return temp;
    }

 	//输出链表
    public void output(){
        ListNode temp=head;
        while (temp!=null){
            System.out.println(temp.val);
            temp=temp.nextNode;
        }
    }

如果不考虑插入删除操作之前的查找元素过程,只考虑纯粹的插入和删除操作,时间复杂度是O(1)

链表优势:灵活地进行插入和删除操作

劣势:查找元素困难

栈:是一种线性数据结构,它就像放入乒乓球的圆筒容器,栈中元素只能先入后出(FILO),最早进入栈中的元素存放的位置叫作栈底(bottom),最后进入的元素存放的位置叫作栈顶(top)

栈操作:入栈(push)和出栈(pop)

class ArrayStack{
	private int maxSize;
	private int[] stack;
	private int top = -1;
	
	
	public ArrayStack(int maxSize) {
		this.maxSize=maxSize;
		stack = new int[this.maxSize];
		
	}
	
    //栈满
	public boolean isFull() {
		return top == maxSize -1;
		
	}
    //栈空
	public boolean isEmpty() {
		return top == -1;
		
	}
	//出栈
	public void push(int value) {
		if(isFull()) {
			System.out.println("栈满");
		}
		top++;
		stack[top] =value;
		}
	//出栈
	public int pop() {
		if(isEmpty()) {
			throw new RuntimeException("栈空");
		}
		int value = stack[top];
		top--;
		return value;
	}
	//遍历栈
	public void list() {
		if(isEmpty()) {
			System.out.println("栈空,没有数据");
			return;
		}
		for(int i = top;i>=0;i--) {
			System.out.printf("stack[%d]=%d\n",i,stack[i]);
		}
	}
}

入栈和出栈的时间复杂度都为O(1)

队列:是一种线性数据结构,它的特征和行使车辆的单行隧道很相似。队列中元素只能先入先出(FIFO),队列的出口端叫作队头(front),队列的入口端叫作队尾(rear)。

循环队列:当(队尾下标+1)%数组长度=队头下标时,表示队列真的满了,队尾指针指向的位置不存放元素

队列操作:入队(enqueue)出队(dequeue)

循环队列入队和出队的时间复杂度都为O(1)

 	//循环队列
    private int[] array;
    private int front;
    private int rear;
    
    public MyQueue(int capacity){
        this.array=new int[capacity];
    }
    
    //入队
    public void enQueue(int element) throws Exception{
        if ((rear+1)%array.length==front){
            throw new Exception("队列已满");
        }
        array[rear]=element;
        rear=(rear+1)%array.length;
    }
    
    //出队
    public int deQueue()throws Exception{
        if (rear==front){
            throw new Exception("队列已满");
        }
        int deQueueElement=array[front];
        front=(front+1)%array.length;
        return deQueueElement;
    }
    
    //输出队列
    public void output(){
        for (int i=front;i!=rear;i=(i+1)%array.length){
            System.out.println(array[i]);
        }
    }

散列表:(哈希表),这种数据结构提供了键和值的映射关系

哈希函数:在不同语言中,哈希函数实现方式不一样。Java通常用集合HashMap(键值对叫Entry),每一个对象都有属于自己的hashcode,比如index=HashCode(Key)%Array.length

哈希冲突:解决方法1-开方寻址法:当一个key通过哈希函数获得对应的数组下标已被占用时,我们可以寻找下一个空档位置 ;解决方法2-链表法:(被运用在HashMap中):HashMap数组的每一个元素不仅是一个Entry对象,还是一个链表的头节点。每一个Entry对象通过next指针指向它的下一个Entry节点。当新来的Entry映射到与之冲突的数组位置时,只需要插入到对应的链表中即可。

散列表写操作(put):通过哈希函数把key转换为数组下标;把值填到数组小标对应位置。

散列表读操作(get):通过哈希函数把key转化为数组下标;找到数组下标对应的元素,如果是就找到了,如果不是可以顺着链表往下找

散列表扩容(resize):对于JDK中的散列表实现类HashMap来说,影响其扩容的因素有两个:1Capacity(HashMap的当前长度) 2LoadFactor(即HashMap的负载因子,默认值为0.75f)衡量HashMap需要扩容条件如下HashMap.Size>=Capacity*LoadFactor 。而且扩容又有两步:1扩容(创建一个新Entry空数组,长度为原来两倍)2重新Hash(遍历原Entry数组,将所有的Entry重新Hash到新数组中)

posted @ 2021-10-16 18:35  fao99  阅读(37)  评论(0)    收藏  举报