热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

算法和数据结构数据结构篇:数组,链表,栈和队列

生活最近还是在学习flask和java,java马上也快看完了,然后就准备开始复习一下数据结构,同时也预习一下算法。都说程序数据结构算法
生活

最近还是在学习flask和java,java马上也快看完了,然后就准备开始复习一下数据结构,同时也预习一下算法。都说程序=数据结构+算法,确实,面对不同问题选择好的数据结构和算法去编程往往能达到最高的效率,经常看到那些简短却又十分完美的代码都会十分佩服,所以决定开始好好复习一下大二学的数据结构(从树那里开始就有点忘了,哈哈)然后学学算法,体会一下那些精妙的思想。
对于数据结构和算法的基本概念,还有时间复杂度,空间复杂度什么的我就不多啰嗦了,看到好多人写的博客真的讲的很好,有兴趣的可以去搜索一下啦。

数据结构部分

我想先从数据结构开始,回顾一下我学过的数据结构:线性表,栈和队列,串,数组和广义表,树、二叉树、哈夫曼树、森林,图……还是有很多知识点的。之前学的时候是用c来写的,但既然自学了这么久java,那我就试着用java写写看,所以今天呢我想从简单的数组,链表开始。
数组和链表都是物理结构,分别是顺序存储结构和链式存储结构。那在内存中呢前者就是顺序存储,后者则是随机存储。数组便于人们去查找,但是不方便插入和删除,链表就刚好相反,相对简单的插入和删除,但是查找某个节点就要遍历链表了。栈和队列也是对应的兄弟,一个先进后出,一个先进先出,他们都是可以基于物理结构实现的逻辑结构。说了这么多,对于所有的数据结构,基本的操作都是增删改查,所以我们还是用代码来实践下。

1.数组

插入:对于数组来说,增又可以分为中间插入和尾插。尾插很简单,只要有空余空间,那就把新来的元素放进去即可,所以重点聊聊中间插入。想让一个新来的元素进入数组中,我们需要知道什么?元素的值和插入的位置也就是插入数组的下标。我们得到了该怎么做呢,首先要把这个位置腾出来,那就从这个位置开始把所有元素往后挪一位,数组大小加一**(注意:判断数组是否越界)**,然后用新加元素的值赋给这个空出来的位置。

删除:删除刚好和插入相反,从这个位置开始到最后每个元素往前移一位,并且数组大小减一。
查找:利用数组下标
修改:利用数组下标直接赋值修改
所以我就写写数组的实现和增删的代码

package 数组的插入;
//数组的插入
public class Myarray {private int[] array;private int size;public Myarray(int capacity) {this.array&#61;new int[capacity];size&#61;0;}//数组的插入public void insert(int element,int index) throws Exception{if(index<0||index>size){throw new IndexOutOfBoundsException("超出数组范围");}if(size>&#61;array.length) {resize();}for(int i&#61;size-1;i>&#61;index;i--){array[i&#43;1]&#61;array[i];}array[index]&#61;element;size&#43;&#61;1;} //数组内元素的删除1:index开始从左往右元素位置依次向左挪一位public int delete(int index) throws Exception{if(index<0||index>&#61;size){throw new IndexOutOfBoundsException("超出数组范围");}int deleted_element&#61;array[index];for(int i&#61;index;i&#61;size){throw new IndexOutOfBoundsException("超出数组范围");}int deleted_element&#61;array[index];array[index]&#61;array[size-1];size-&#61;1;return deleted_element;}public void resize(){int[] arraynew &#61; new int[array.length*2];//对原数组范围扩大两倍System.arraycopy(array, 0, arraynew, 0, array.length);//从旧数组复制到新的数组中array&#61;arraynew;}public void print(){for(int i&#61;0;i

我这里对数组越界做了处理&#xff0c;如果数组发生越界&#xff0c;我就将数组的大小变为原来的两倍。运行的结果
在这里插入图片描述

2.链表

插入&#xff1a;链表的插入相对于数组来说就比较方便&#xff0c;不用大面积的更换元素&#xff0c;插入新的节点只需要得到插入位置&#xff0c;然后找到它的前一个节点&#xff0c;将前一个节点的下一节点改为新添加的节点&#xff0c;新添加节点的下一节点变为原节点的下一节点。大概就是图里这个意思&#xff0c;画图丑别介意。
在这里插入图片描述
删除&#xff1a;和插入相反&#xff0c;得到前一个节点后再获取它的下下个节点&#xff0c;并且把前一个节点的下一个节点变为它的下下个节点。

查找&#xff1a;从头节点开始遍历数组&#xff0c;没找到需要的位置就找它的下一个节点&#xff0c;直到找到尾节点。

修改&#xff1a;根据查找到的节点&#xff0c;修改它的data

package 链表;
//实现单链表
public class Mylist {private static class Node{int data;Node next;Node(int data){ this.data&#61;data;}}private Node head;private Node last;private int size;//链表插入元素public void insert(int data,int index) throws Exception{if(index<0||index>size){throw new IndexOutOfBoundsException("超出链表范围");}Node insertnode&#61;new Node(data);if(size&#61;&#61;0){//插入头部head&#61;insertnode;last&#61;insertnode;}else if(size&#61;&#61;index){//插入尾部last.next&#61;insertnode;last&#61;insertnode;}else{//插入中间Node prevnode&#61;get(index-1);Node nextnode&#61;prevnode.next;prevnode.next&#61;insertnode;insertnode.next&#61;nextnode;}size&#43;&#61;1;}//链表删除元素public Node remove(int index) throws Exception{if(index<0||index>size){throw new IndexOutOfBoundsException("超出链表范围");}Node removenode&#61;null;if(index&#61;&#61;0){//删除头节点removenode&#61;head;head&#61;head.next;}else if(index&#61;&#61;size-1){//删除尾节点Node prevnode&#61;get(index-1);removenode&#61;prevnode.next;prevnode.next&#61;null;last&#61;prevnode;}else{//删除中间节点Node prevnode &#61;get(index-1);Node nextnode&#61;prevnode.next.next;removenode&#61;prevnode.next;prevnode.next&#61;nextnode;}size-&#61;1;return removenode;}//链表查找元素public Node get(int index) throws Exception{if(index<0||index>size){throw new IndexOutOfBoundsException("超出链表范围");}Node t&#61;head;for(int i&#61;0;i

运行结果&#xff1a;
在这里插入图片描述

3.栈和队列

前面说过&#xff0c;他们的区别并不大&#xff0c;只是一个是先进后出&#xff0c;一个是先进先出&#xff0c;如果比喻的话&#xff0c;可以把栈比喻成往瓶子里装糖果&#xff0c;最先装进去的在最底下&#xff0c;最后才会出来。而队列就像是车过隧道&#xff0c;先进去的车先出山洞。这两种数据结构可以用数组实现&#xff0c;也可以用链表实现&#xff0c;只要按照它们的定义&#xff0c;按规则来存或取就行&#xff0c;并不难。我想重点聊的是循环队列。假设我们用数组来实现队列&#xff0c;几次出队操作后前面的位置全部空出来了&#xff0c;而有可能数组已经满了&#xff0c;无法再入队&#xff0c;那么这时候前面出队的位置就浪费掉了。那我们能不能想一种方法来填满这个空出来的空间呢&#xff0c;肯定可以的&#xff0c;这就需要循环队列了。

循环队列&#xff1a;几次出队操作后&#xff0c;我们可以把队尾的指针重新回到数组的首位&#xff0c;那样入队就可以利用出队留下的空余空间。那什么时候队列真的满了呢&#xff0c;其实就是队尾指针后一个就是队头指针&#xff0c;那我们判断式的代码一般就是&#xff08;队尾&#43;1&#xff09;%数组大小&#61;队头。&#xff08;为什么不直接加一&#xff1f;有可能队尾指针在最后一个&#xff0c;队头指针在第一个&#xff09;
那我们就来实现这个循环队列

package 循环队列;
//用数组实现循环队列
public class Myqueue {public Myqueue(int capacity){this.array&#61;new int[capacity];}private int[] array;private int front;//队头private int rear;//队尾//入队enqueuepublic void enqueue(int element) throws Exception{if((rear&#43;1)%array.length&#61;&#61;front)throw new Exception("队列已经满了");array[rear]&#61;element;rear&#61;(rear&#43;1)%array.length;}//出队dequeuepublic int dequeue() throws Exception{if(rear&#61;&#61;front)throw new Exception("队列已经空了");int del&#61;array[front];front&#61;(front&#43;1)%array.length;return del;}//队列的输出public void print(){System.out.println("队列中的元素为:");for(int i&#61;front;i!&#61;rear;i&#61;(i&#43;1)%array.length){System.out.printf("%d\t",array[i]);}System.out.println();}public static void main(String[] args) throws Exception {// TODO Auto-generated method stubMyqueue que&#61;new Myqueue(6);que.enqueue(3);que.enqueue(5);que.enqueue(2);que.enqueue(8);que.dequeue();que.dequeue();que.enqueue(12);que.print();}}

在这里插入图片描述

总结

数据结构和算法其实并没有想象的那么可怕&#xff0c;只要你愿意动脑筋去多想想&#xff0c;多联系一下实际生活来体会&#xff0c;不懂的时候多画画图来模拟。当时学数据结构的时候链表啥的也是一直没搞清楚&#xff0c;但是现在回过头再来复习&#xff0c;感觉简单了很多。所以&#xff0c;千万不要有抗拒抵制心理&#xff0c;你愿意和它交朋友&#xff0c;才能更好了解它。后期呢我也会补充算法和数据结构这一块的内容&#xff0c;争取用c,c&#43;&#43;,java,python全部都实现一遍。不过这段时间很忙就没时间经常更新了&#xff0c;争取每天学的都写点东西记录保存&#xff0c;最后有时间整合一下发。


推荐阅读
  • 本题要求在一组数中反复取出两个数相加,并将结果放回数组中,最终求出最小的总加法代价。这是一个经典的哈夫曼编码问题,利用贪心算法可以有效地解决。 ... [详细]
  • 深入解析动态代理模式:23种设计模式之三
    在设计模式中,动态代理模式是应用最为广泛的一种代理模式。它允许我们在运行时动态创建代理对象,并在调用方法时进行增强处理。本文将详细介绍动态代理的实现机制及其应用场景。 ... [详细]
  • 主调|大侠_重温C++ ... [详细]
  • 深入理解Java多线程并发处理:基础与实践
    本文探讨了Java中的多线程并发处理机制,从基本概念到实际应用,帮助读者全面理解并掌握多线程编程技巧。通过实例解析和理论阐述,确保初学者也能轻松入门。 ... [详细]
  • 本文详细介绍了Java中实现异步调用的多种方式,包括线程创建、Future接口、CompletableFuture类以及Spring框架的@Async注解。通过代码示例和深入解析,帮助读者理解并掌握这些技术。 ... [详细]
  • 本文档汇总了Python编程的基础与高级面试题目,涵盖语言特性、数据结构、算法以及Web开发等多个方面,旨在帮助开发者全面掌握Python核心知识。 ... [详细]
  • 深入解析Java枚举及其高级特性
    本文详细介绍了Java枚举的概念、语法、使用规则和应用场景,并探讨了其在实际编程中的高级应用。所有相关内容已收录于GitHub仓库[JavaLearningmanual](https://github.com/Ziphtracks/JavaLearningmanual),欢迎Star并持续关注。 ... [详细]
  • 实用正则表达式有哪些
    小编给大家分享一下实用正则表达式有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下 ... [详细]
  • 本文探讨了在Java中如何正确地将多个不同的数组插入到ArrayList中,避免所有数组在插入后变得相同的问题。我们将分析代码中的问题,并提供解决方案。 ... [详细]
  • 本文介绍如何从字符串中移除大写、小写、特殊、数字和非数字字符,并提供了多种编程语言的实现示例。 ... [详细]
  • 深入解析Java虚拟机(JVM)架构与原理
    本文旨在为读者提供对Java虚拟机(JVM)的全面理解,涵盖其主要组成部分、工作原理及其在不同平台上的实现。通过详细探讨JVM的结构和内部机制,帮助开发者更好地掌握Java编程的核心技术。 ... [详细]
  • 深入解析ArrayList与LinkedList的差异
    本文详细对比了Java中ArrayList和LinkedList两种常用集合类的特性、性能及适用场景,通过代码示例进行测试,并结合实际应用场景分析其优缺点。 ... [详细]
  • 深入剖析JVM垃圾回收机制
    本文详细探讨了Java虚拟机(JVM)中的垃圾回收机制,包括其意义、对象判定方法、引用类型、常见垃圾收集算法以及各种垃圾收集器的特点和工作原理。通过理解这些内容,开发人员可以更好地优化内存管理和程序性能。 ... [详细]
  • 本文探讨了如何通过一系列技术手段提升Spring Boot项目的并发处理能力,解决生产环境中因慢请求导致的系统性能下降问题。 ... [详细]
  • ------------------------------————————————————————————————1.定义一个类,实现与被增强对象相同的接口2.在类中定义一个对象,记住被增强 ... [详细]
author-avatar
Smitty
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有