数据结构和算法 (二)数据结构基础、线性表、栈和队列、数组和字符串

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

一、数据结构概念

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

二、线性表

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

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来简单实现一下数组。
数组是一种引用类型的对象,我们可以像下面这样的方式来声明数组:

[java] view plain copy

  1. int a[];
  2. int[] b;
  3. int []c;

[java] view plain copy

  1. a = new int[10];

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

[java] view plain copy

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


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

[java] view plain copy

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

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

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

[java] view plain copy

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

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

[java] view plain copy

  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. }

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

[java] view plain copy

  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中附有源码,感兴趣的同学可以读读。下面我简单介绍下单链表。

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

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

[java] view plain copy

  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)的结构,也就是说只能在表头进行删除,在表尾进行添加,下面我们实现一个简单的队列。

[java] view plain copy

  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)的数据结构,我们采用单链表实现一个栈。

[java] view plain copy

  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中的实现,敬请读者朋友们持续关注!

借鉴:http://blog.csdn.net/zhangerqing/article/details/8796518

时间: 2024-10-21 19:30:36

数据结构和算法 (二)数据结构基础、线性表、栈和队列、数组和字符串的相关文章

数据结构和算法学习总结04 线性表---栈

栈 栈(Stack)是特殊的线性表,是只允许在一端进行插入和删除的线性表. 允许插入和删除的叫栈顶,反之则是栈底. 栈的插入称为进栈,删除称为出栈. 特性是:后进先出,所以栈也叫后进先出表,简称LIFO表(Last In First Out). 因为栈是线性表,所以也有顺序表和链表两种形式,一般我们常用顺序表. 从代码中可以看出:与顺序表相比实际上就是插入和删除操作发生了改变. #include <iostream> using namespace std; const int Stack_S

数据结构与算法(三)-线性表之静态链表

前言:前面介绍的线性表的顺序存储结构和链式存储结构中,都有对对象地引用或指向,也就是编程语言中有引用或者指针,那么在没有引用或指针的语言中,该怎么实现这个的数据结构呢? 一.简介 定义:用数组代替指针或引用来描述单链表,即用数组描述的链表叫做静态链表,这种描述方法叫做游标实现法: 上面的静态链表图有两个数组游标和数据,其中数据数组存储数据,而游标数组存储同下标为数据的下一个数据的下标值,简单模拟一下静态链表遍历的过程: 先查看下标为999的游标数组值:1: 根据游标数组值1,查找下标为1的数据:

数据结构与算法(四)-线性表之循环链表

前言:前面几篇介绍了线性表的顺序和链式存储结构,其中链式存储结构为单向链表(即一个方向的有限长度.不循环的链表),对于单链表,由于每个节点只存储了向后的指针,到了尾部标识就停止了向后链的操作.也就是说只能向后走,如果走过了,就回不去了,还得重头开始遍历,所以就衍生出了循环链表 一.简介 定义:将单链表中中断结点的指针端有空指针改为指向头结点,就使整个单链表形成一个环,这种头尾详解的单链表称为单循环链表,简称循环链表: 特性: 若链表为空,则头结点的next结点还是指向其本身,即head.next

数据结构和算法学习总结03 线性表---队列

队列 队列(Queue)是限定只能在表的一端进行插入和在另一端进行删除操作的线性表. 与栈的比较: 1.队列先进先出,栈先进后出. 2.从"数据结构"的角度看,它们都是线性结构,即数据元素之间的关系相同. 但它们是完全不同的数据类型.除了它们各自的基本操作集不同外,主要区别是对插入和删除操作的"限定": 栈是限定只能在表的一端进行插入和删除操作的线性表:队列是限定只能在表的一端进行插入和在另一端进行删除操作的线性表. 队列同样分为顺序队列和链式队列,我们一般用链式队

数据结构和算法学习总结03 线性表---顺序表

线性表是最简单.也是最基本的一种线性数据结构. 它有两种存储表示法:顺序表和链表,最基本的操作是插入.删除和查找等. 顺序表的基本操作如下: #include <stdio.h> #include <stdlib.h> #define EXIST 1 //存在数据元素,不为空 #define EMPTY 0 //为空 #define OK 1 //成功 #define ERROR 0 //出现错误 #define OVERFLOW -1 //元素溢出 const int LIST_

《数据结构与算法》第二章 线性表

线性表 定义: 由n个特性相同 数据元素(即可有很多数据项)构成的有限序列,同时相邻数据元素之间存在 序偶 关系. 线性表中元素个数就是 表长 . 特点: ·存在唯一一个被称为 “第一个” 的数据元素(线性起点.起始结点): ·存在唯一一个被称为 “最后一个” 的数据元素(线性终点.终端结点): ·除第一个以外,结构中的每个数据元素均 只有一个前驱 : ·除最后一个外,结构中的每个数据元素均 只有一个后继 . 顺序表示和实现 线性表的顺序表示指的是用一组地址连续的存储单元一次存储线性表的数据元素

2 限定性线性表——栈与队列

1 栈与队列     1.1 包含min函数的栈 定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数 在该栈中,调用min.push和pop方法 要求时间复杂度均为O(1) 算法思想: 要求时间复杂度均为 O(1),增加辅助空间实现,即增加一个辅助栈存储min值 例如:data 中依次入栈 5, 4, 3, 8, 10, 11, 12, 1, 则 min 中依次入栈 5, 4, 3,no,no, no, no, 1. no 代表此次不如栈,如果入栈的元素小于等于 min 中的栈

数据结构学习(二)、线性表的练习

学习新的知识,只是单纯的看书,当时你跟随着作者的思路可能很容易就懂了,脱离书本后,发现似懂非懂, 到了实际的应用时,更加会感觉似是而非.所以,适量的练习,是非常有利于理解新知识的. 1.将一个带结点的单向链表逆序. 根据是否改变头结点分为2种方法. 变换头结点 思路: 1.声明三指针变量p,q,r,其中p指向头结点,q指向第一个结点: 2. 循环(q不为空) 将q->next赋值给r 将q->next设为p             将q的值赋给p 将q赋值给p 将r赋值给q 3. 将原头指针指

数据结构与算法二

1.课程安排表: 1. 线性表 2. 字符串 3. 栈和队列 4.树 5.查找 6.排序 7.暴力枚举法 8.广度优先搜索 9.深度优先搜索 10.分治 11.贪心 12.动态规划 13.图 14.数学方法与常见模型 15.大整数运算 16. 基础功能 2.   编程技巧: 1.把较大的数组放在main 函数(全局变量)外,作为全局变量,这样可以防止栈溢出,因为栈的大小是有限制的.GCC (C编译器) 段错误 2.如果能够预估栈,队列的上限,则不要用stack,queue,使用数组来模拟,这样速

数据结构回顾之顺序存储结构中的线性表(栈与队列顺序线性表实现)

说到数据结构呢,对于一个Coder来说还是蛮重要的啦,每次看数据结构的东西都有新的收获,这两天在回顾数据结构的知识.当然啦,虽然数据结构有些是理论的东西,如果好好的理解数据结构的东西还是少不了的代码的支撑的.数据结构简单的来说吧,可以分为两大类,一个是数据的"物理存储结构",另一种是数据的"逻辑存储结构".数据的"物理存储结构"又可分为顺序的和链式的(下面将会结合着代码打印内存地址的形式来观察物理存储结构). 逻辑存储结构又可分为集合,线性, 树