Java面试宝典之数据结构基础 —— 线性表篇

作者:egg

邮箱:xtfggef@gmail.com

微博:http://weibo.com/xtfggef

博客:http://blog.csdn.net/zhangerqing(转载请说明出处)

这部分内容作为计算机专业最基础的知识,几乎被所有企业选中用来作考题,因此,本章我们从本章开始,我们将从基础方面对数据结构进行讲解,内容主要是线性表,包括栈、队列、数组、字符串等,主要讲解基础知识,如概念及简单的实现代码,非线性结构我们在后面的文章中给出。过程中有任

何问题,请联系本人:http://weibo.com/xtfggef

一、数据结构概念

用我的理解,数据结构包含数据和结构,通俗一点就是将数据按照一定的结构组合起来,不同的组合方式会有不同的效率,使用不同的场景,如此而已。比如我们最常用的数组,就是一种数据结构,有独特的承载数据的方式,按顺序排列,其特点就是你可以根据下标快速查找元素,但是因为在数组中插入和删除元素会有其它元素较大幅度的便宜,所以会带来较多的消耗,所以因为这种特点,使得数组适合:查询比较频繁,增、删比较少的情况,这就是数据结构的概念。数据结构包括两大类:线性结构和非线性结构,线性结构包括:数组、链表、队列、栈等,非线性结构包括树、图、表等及衍生类结构。本章我们先讲解线性结构,主要从数组、链表、队列、栈方面进行讨论,非线性数据结构在后面会继续讲解。

二、线性表

线性表是最基本、最简单、也是最常用的一种数据结构。线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。线性表的逻辑结构简单,便于实现和操作。因此,线性表这种数据结构在实际应用中是广泛采用的一种数据结构。其基本操作主要有:

1)MakeEmpty(L) 这是一个将L变为空表的方法
   2)Length(L) 返回表L的长度,即表中元素个数 
   3)Get(L,i) 这是一个函数,函数值为L中位置i处的元素(1≤i≤n)
   4)Prev(L,i) 取i的前驱元素
   5)Next(L,i) 取i的后继元素
   6)Locate(L,x) 这是一个函数,函数值为元素x在L中的位置
   7)Insert(L,i,x)在表L的位置i处插入元素x,将原占据位置i的元素及后面的元素都向后推一个位置
   8)Delete(L,p) 从表L中删除位置p处的元素
   9)IsEmpty(L) 如果表L为空表(长度为0)则返回true,否则返回false
   10)Clear(L)清除所有元素
   11)Init(L)同第一个,初始化线性表为空
   12)Traverse(L)遍历输出所有元素
   13)Find(L,x)查找并返回元素
   14)Update(L,x)修改元素
   15)Sort(L)对所有元素重新按给定的条件排序
   16) strstr(string1,string2)用于字符数组的求string1中出现string2的首地址

不管采用哪种方式实现线性表,至少都应该具有上述这些基本方法,下面我会将下数据结构的基本实现方式。

三、基础数据结构

数据结构是一种抽象的数据类型(ADT),可以这么说,我们可以采用任意的方式实现某种数据结构,只要符合将要实现的数据结构的特点,数据结构就是一种标准,我们可以采用不同的方式去实现,最常用的两种就是数组和链表(包括单链表、双向链表等)。数组是非常常见的数据类型,在任何一种语言里都有它的实现,我们这里采用Java来简单实现一下数组。
数组是一种引用类型的对象,我们可以像下面这样的方式来声明数组:

  1. int a[];
  2. int[] b;
  3. int []c;
        a = new int[10];

总结起来,声明一个数组有基本的三个因素:类型、名称、下标,Java里,数组在格式上相对灵活,下标和名称可以互换位置,前三种情况我们可以理解为声明一个变量,后一种为其赋值。或者像下面这样,在声明的时候赋值:

  1. int c[] = {2,3,6,10,99};
  2. int []d = new int[10];

我稍微解释一下,其实如果只执行:int[] b,只是在栈上创建一个引用变量,并未赋值,只有当执行d = new int[10]才会在堆上真正的分配空间。上述第一行为静态初始化,就是说用户指定数组的内容,有系统计算数组的大小,第二行恰恰相反,用户指定数组的大小,由系统分配初始值,我们打印一下数组的初始值:

  1. int []d = new int[10];
  2. System.out.println(d[2]);

结果输出0,对于int类型的数组,默认的初始值为0.

但是,绝对不可以像下面这样:

       int e[10] = new int[10];

无法通过编译,至于为什么,语法就是这样,这是一种规范,不用去想它。
我们可以通过下标来检索数组。下面我举个简单的例子,来说明下数组的用法。

  1. public static void main(String[] args) {
  2. String name[];
  3. name = new String[5];
  4. name[0] = "egg";
  5. name[1] = "erqing";
  6. name[2] = "baby";
  7. for (int i = 0; i < name.length; i++) {
  8. System.out.println(name[i]);
  9. }
  10. }

这是最简单的数组声明、创建、赋值、遍历的例子,下面写个增删的例子。

  1. package com.xtfggef.algo.array;
  2. public class Array {
  3. public static void main(String[] args) {
  4. int value[] = new int[10];
  5. for (int i = 0; i < 10; i++) {
  6. value[i] = i;
  7. }
  8. // traverse(value);
  9. // insert(value, 666, 5);
  10. delete(value, 3);
  11. traverse(value);
  12. }
  13. public static int[] insert(int[] old, int value, int index) {
  14. for (int k = old.length - 1; k > index; k--)
  15. old[k] = old[k - 1];
  16. old[index] = value;
  17. return old;
  18. }
  19. public static void traverse(int data[]) {
  20. for (int j = 0; j < data.length; j++)
  21. System.out.print(data[j] + " ");
  22. }
  23. public static int[] delete(int[] old, int index) {
  24. for (int h = index; h < old.length - 1; h++) {
  25. old[h] = old[h + 1];
  26. }
  27. old[old.length - 1] = 0;
  28. return old;
  29. }
  30. }

简单写一下,主要想说明数组中删除和增加元素的原理:增加元素,需要将index后面的依次向后移动,然后将值插入index位置,删除则将后面的值依次向前移动,较简单。

要记住:数组是表示相同类型的一类数据的集合,下标从0开始,就行了。

数组实现的线下表可以参考ArrayList,在JDK中附有源码,感兴趣的同学可以读读。下面我简单介绍下单链表。

单链表是最简单的链表,有节点之间首尾连接而成,简单示意如下:

除了头节点,每个节点包含一个数据域一个指针域,除了头、尾节点,每个节点的指针指向下一个节点,下面我们写个例子操作一下单链表。

  1. package com.xtfggef.algo.linkedlist;
  2. public class LinkedList<T> {
  3. /**
  4. * class node
  5. * @author egg
  6. * @param <T>
  7. */
  8. private static class Node<T> {
  9. T data;
  10. Node<T> next;
  11. Node(T data, Node<T> next) {
  12. this.data = data;
  13. this.next = next;
  14. }
  15. Node(T data) {
  16. this(data, null);
  17. }
  18. }
  19. // data
  20. private Node<T> head, tail;
  21. public LinkedList() {
  22. head = tail = null;
  23. }
  24. /**
  25. * judge the list is empty
  26. */
  27. public boolean isEmpty() {
  28. return head == null;
  29. }
  30. /**
  31. * add head node
  32. */
  33. public void addHead(T item) {
  34. head = new Node<T>(item);
  35. if (tail == null)
  36. tail = head;
  37. }
  38. /**
  39. * add the tail pointer
  40. */
  41. public void addTail(T item) {
  42. if (!isEmpty()) {
  43. tail.next = new Node<T>(item);
  44. tail = tail.next;
  45. } else {
  46. head = tail = new Node<T>(item);
  47. }
  48. }
  49. /**
  50. * print the list
  51. */
  52. public void traverse() {
  53. if (isEmpty()) {
  54. System.out.println("null");
  55. } else {
  56. for (Node<T> p = head; p != null; p = p.next)
  57. System.out.println(p.data);
  58. }
  59. }
  60. /**
  61. * insert node from head
  62. */
  63. public void addFromHead(T item) {
  64. Node<T> newNode = new Node<T>(item);
  65. newNode.next = head;
  66. head = newNode;
  67. }
  68. /**
  69. * insert node from tail
  70. */
  71. public void addFromTail(T item) {
  72. Node<T> newNode = new Node<T>(item);
  73. Node<T> p = head;
  74. while (p.next != null)
  75. p = p.next;
  76. p.next = newNode;
  77. newNode.next = null;
  78. }
  79. /**
  80. * delete node from head
  81. */
  82. public void removeFromHead() {
  83. if (!isEmpty())
  84. head = head.next;
  85. else
  86. System.out.println("The list have been emptied!");
  87. }
  88. /**
  89. * delete frem tail, lower effect
  90. */
  91. public void removeFromTail() {
  92. Node<T> prev = null, curr = head;
  93. while (curr.next != null) {
  94. prev = curr;
  95. curr = curr.next;
  96. if (curr.next == null)
  97. prev.next = null;
  98. }
  99. }
  100. /**
  101. * insert a new node
  102. * @param appointedItem
  103. * @param item
  104. * @return
  105. */
  106. public boolean insert(T appointedItem, T item) {
  107. Node<T> prev = head, curr = head.next, newNode;
  108. newNode = new Node<T>(item);
  109. if (!isEmpty()) {
  110. while ((curr != null) && (!appointedItem.equals(curr.data))) {
  111. prev = curr;
  112. curr = curr.next;
  113. }
  114. newNode.next = curr;
  115. prev.next = newNode;
  116. return true;
  117. }
  118. return false;
  119. }
  120. public void remove(T item) {
  121. Node<T> curr = head, prev = null;
  122. boolean found = false;
  123. while (curr != null && !found) {
  124. if (item.equals(curr.data)) {
  125. if (prev == null)
  126. removeFromHead();
  127. else
  128. prev.next = curr.next;
  129. found = true;
  130. } else {
  131. prev = curr;
  132. curr = curr.next;
  133. }
  134. }
  135. }
  136. public int indexOf(T item) {
  137. int index = 0;
  138. Node<T> p;
  139. for (p = head; p != null; p = p.next) {
  140. if (item.equals(p.data))
  141. return index;
  142. index++;
  143. }
  144. return -1;
  145. }
  146. /**
  147. * judge the list contains one data
  148. */
  149. public boolean contains(T item) {
  150. return indexOf(item) != -1;
  151. }
  152. }

单链表最好玩儿的也就是增加和删除节点,下面的两个图分别是用图来表示单链表增、删节点示意,看着图学习,理解起来更加容易!

接下来的队列和栈,我们分别用不同的结构来实现,队列用数组,栈用单列表,读者朋友对此感兴趣,可以分别再用不同的方法实现。

四、队列
队列是一个常用的数据结构,是一种先进先出(First In First Out, FIFO)的结构,也就是说只能在表头进行删除,在表尾进行添加,下面我们实现一个简单的队列。

  1. package com.xtfggef.algo.queue;
  2. import java.util.Arrays;
  3. public class Queue<T> {
  4. private int DEFAULT_SIZE = 10;
  5. private int capacity;
  6. private Object[] elementData;
  7. private int front = 0;
  8. private int rear = 0;
  9. public Queue()
  10. {
  11. capacity = DEFAULT_SIZE;
  12. elementData = new Object[capacity];
  13. }
  14. public Queue(T element)
  15. {
  16. this();
  17. elementData[0] = element;
  18. rear++;
  19. }
  20. public Queue(T element , int initSize)
  21. {
  22. this.capacity = initSize;
  23. elementData = new Object[capacity];
  24. elementData[0] = element;
  25. rear++;
  26. }
  27. public int size()
  28. {
  29. return rear - front;
  30. }
  31. public void add(T element)
  32. {
  33. if (rear > capacity - 1)
  34. {
  35. throw new IndexOutOfBoundsException("the queue is full!");
  36. }
  37. elementData[rear++] = element;
  38. }
  39. public T remove()
  40. {
  41. if (empty())
  42. {
  43. throw new IndexOutOfBoundsException("queue is empty");
  44. }
  45. @SuppressWarnings("unchecked")
  46. T oldValue = (T)elementData[front];
  47. elementData[front++] = null;
  48. return oldValue;
  49. }
  50. @SuppressWarnings("unchecked")
  51. public T element()
  52. {
  53. if (empty())
  54. {
  55. throw new IndexOutOfBoundsException("queue is empty");
  56. }
  57. return (T)elementData[front];
  58. }
  59. public boolean empty()
  60. {
  61. return rear == front;
  62. }
  63. public void clear()
  64. {
  65. Arrays.fill(elementData , null);
  66. front = 0;
  67. rear = 0;
  68. }
  69. public String toString()
  70. {
  71. if (empty())
  72. {
  73. return "[]";
  74. }
  75. else
  76. {
  77. StringBuilder sb = new StringBuilder("[");
  78. for (int i = front ; i < rear ; i++ )
  79. {
  80. sb.append(elementData[i].toString() + ", ");
  81. }
  82. int len = sb.length();
  83. return sb.delete(len - 2 , len).append("]").toString();
  84. }
  85. }
  86. public static void main(String[] args){
  87. Queue<String> queue = new Queue<String>("ABC", 20);
  88. queue.add("DEF");
  89. queue.add("egg");
  90. System.out.println(queue.empty());
  91. System.out.println(queue.size());
  92. System.out.println(queue.element());
  93. queue.clear();
  94. System.out.println(queue.empty());
  95. System.out.println(queue.size());
  96. }
  97. }

队列只能在表头进行删除,在表尾进行增加,这种结构的特点,适用于排队系统。

五、栈

栈是一种后进先出(Last In First Out,LIFO)的数据结构,我们采用单链表实现一个栈。

  1. package com.xtfggef.algo.stack;
  2. import com.xtfggef.algo.linkedlist.LinkedList;
  3. public class Stack<T> {
  4. static class Node<T> {
  5. T data;
  6. Node<T> next;
  7. Node(T data, Node<T> next) {
  8. this.data = data;
  9. this.next = next;
  10. }
  11. Node(T data) {
  12. this(data, null);
  13. }
  14. }
  15. @SuppressWarnings("rawtypes")
  16. static LinkedList list = new LinkedList();
  17. @SuppressWarnings("unchecked")
  18. public T push(T item) {
  19. list.addFromHead(item);
  20. return item;
  21. }
  22. public void pop() {
  23. list.removeFromHead();
  24. }
  25. public boolean empty() {
  26. return list.isEmpty();
  27. }
  28. public int search(T t) {
  29. return list.indexOf(t);
  30. }
  31. public static void main(String[] args) {
  32. Stack<String> stack = new Stack<String>();
  33. System.out.println(stack.empty());
  34. stack.push("abc");
  35. stack.push("def");
  36. stack.push("egg");
  37. stack.pop();
  38. System.out.println(stack.search("def"));
  39. }
  40. }

本章的内容都是很基础的,重在让读者朋友们理解数据结构的概念,下章开始,我们会介绍树、二叉树等Java中的实现,敬请读者朋友们持续关注!

作者:egg

邮箱:xtfggef@gmail.com

微博:http://weibo.com/xtfggef

博客:http://blog.csdn.net/zhangerqing(转载请说明出处)

有问题,请依照上述联系方式联系作者,欢迎读者及时提出建议,谢谢!

Java面试宝典系列之基础面试题String、变量、类与对象、集合类、SSH(三)相关推荐

  1. Java面试宝典系列之基础面试题String、变量、类与对象、集合类、SSH(一)

    作者:egg 邮箱:xtfggef@gmail.com 微博:http://weibo.com/xtfggef 博客:http://blog.csdn.net/zhangerqing(转载请说明出处) ...

  2. Java面试宝典系列之基础面试题String、变量、类与对象、集合类、SSH(二)

    作者:egg 邮箱:xtfggef@gmail.com 微博:http://weibo.com/xtfggef 博客:http://blog.csdn.net/zhangerqing(转载请说明出处) ...

  3. Java面试宝典系列之基础面试题-常见的几种排序算法-插入、选择、冒泡、快排、堆排等

    常见的几种排序算法-插入.选择.冒泡.快排.堆排: https://blog.csdn.net/zhangerqing/article/details/8831542

  4. Java面试宝典系列之面试复习提纲

    Java面试复习提纲 作者:egg 邮箱:xtfggef@gmail.com 微博:http://weibo.com/xtfggef 博客:http://blog.csdn.net/zhangerqi ...

  5. Java面试宝典之:基础篇

    一.   Java基础部分 1.Java多态的具体体现 [必背] 面向对象编程有四个特征:抽象,封装,继承,多态. 多态有四种体现形式: 1. 接口和接口的继承. 2. 类和类的继承. 3. 重载. ...

  6. 【2022最新Java面试宝典】—— Java虚拟机(JVM)面试题(51道含答案)

    目录 一.Java内存模型 1. 我们开发人员编写的Java代码是怎么让电脑认识的 2. 为什么说java是跨平台语言 3. Jdk和Jre和JVM的区别 4. 说一下 JVM由那些部分组成,运行流程 ...

  7. 【2022最新Java面试宝典】—— Java并发编程面试题(123道含答案)

    目录 一.基础知识 1. 为什么要使用并发编程 2. 多线程应用场景 3. 并发编程有什么缺点 4. 并发编程三个必要因素是什么? 5. Java 程序中怎么保证多线程的运行安全? 6. 并行和并发有 ...

  8. 【2022最新Java面试宝典】—— Redis面试题(70道含答案)

    目录 一.概述 1. 什么是Redis? 2. Redis有哪些优缺点? 3. 使用redis有哪些好处? 4. 为什么要用 Redis / 为什么要用缓存 5. 为什么要用 Redis 而不用 ma ...

  9. 【2022最新Java面试宝典】—— MySQL面试题(40道含答案)

    目录 1.MySQL 中有哪几种锁? 2.MySQL 中有哪些不同的表格? 3.简述在MySQL 数据库中 MyISAM 和InnoDB 的区别 4.MySQL 中InnoDB 支持的四种事务隔离级别 ...

最新文章

  1. Linux下查看显示器输出状态以及修改显示器工作模式(复制 or 扩展)
  2. SAP CRM right dimension attribute后台存储
  3. Android之Launcher分析和修改3——Launcher启动和初始化
  4. Java ClassLoader getParent()方法与示例
  5. shell判断文件,目录是否存在或者具有权限 (转载)
  6. Javascript的一种代码结构方式——插件式
  7. vc2013控件第一个程序
  8. 明解C语言(第3版)入门篇 - 第六章练习题解
  9. C语言入门教程(一)
  10. 行贿罪、受贿罪刑事辩护6大要点
  11. MySQL Cluster测试过程中的错误汇总--ERROR 1296 (HY000)等等
  12. 现在应该继续投资公链?还是投资应用级协议?
  13. 国际高中成绩差但是想读名校怎么办(文末附自救指南)
  14. windows 10专业版 1709 使用curl命令
  15. 三分钟了解坚果J10的不足,多方位分析,让你学会挑选投影仪
  16. ctfshow-web78(文件包含)
  17. C# 引用C++动态库方法
  18. [方面级别情感分析]0x01方面级情感分析笔记
  19. MessageBox.Show用法
  20. 基类与派生类中同名成员的关系

热门文章

  1. NBA部署SAP HANA内存数据库
  2. 德力西电气签约永洪科技,数字化赋能电气制造新征程
  3. 重磅推出校园疫情填报系统,永洪BI助力疫情防控
  4. java filereader类_Java FileReader类
  5. python合并列表重新排序_python – 将两个已排序的列表合并为一个更大的排序列表...
  6. [BUUCTF]PWN——ciscn_2019_es_2(栈劫持)
  7. oracle 整个表空间迁移,ORACLE表批量迁移表空间
  8. Python -itertools模块combinations方法
  9. Python多线程的两种实现方式
  10. python 类属性和对象属性--定义和作用域