数据结构与算法(二)——线性表

一、线性表

1、介绍

  由零个或多个数据元素组成的有限序列,有序表
  数据类型:一组性质相同的值的集合及定义在此集合上的一些操作的总称。

2、顺序存储结构

  顺序表:用一组地址连续的存储单元存放数据。
  特点:具有随机存储结构的特点,时间复杂度为o(1)。存、读数据时,不管哪个位置,时间复杂度都是o(1);插入和删除的时间复杂度是o(n)。
  优点:无需为表示元素之间的逻辑关系而增加额外的存储空间;可以快速的存取表中任意位置的元素。
  缺点:插入和删除操作需要移动大量元素;当线性表长度变化较大时,难以确定存储空间的容量;容易造成存储空间的"碎片"。
  常见结构:数组、栈。

3、链式存储结构

  链表:由一个数据域和一个指向下一个元素地址的指针域构成。存放数据的地址不一定连续。链表又主要包括:单(向)链表,单(向)循环链表,双(向)链表,双(向)循环链表。
  常见结构:链表、队列。

4、顺序表与链表区别

  结论:若线性表需要频繁查找,很少进行插入和删除操作,采用顺序表;若需要频繁插入和删除,采用链表。

二、稀疏数组

1、介绍

2、二维数组、稀疏数组相互转化

  代码示例:

 1 public class SparseArray {
 2     // 构建稀疏数组.二维数组 --> 稀疏数组
 3     public int[][] createSparseArr(int[][] chess) {
 4         final int sum = getCount(chess);
 5 
 6         // 创建的稀疏数组
 7         int[][] sparseArr = new int[sum + 1][3];
 8         sparseArr[0][0] = chess.length;
 9         sparseArr[0][1] = chess[0].length;
10         sparseArr[0][2] = sum;
11 
12         // count 用于记录是第几个非 0 数据
13         int count = 0;
14         for (int i = 0; i < chess.length; i++) {
15             for (int j = 0; j < chess[i].length; j++) {
16                 if (chess[i][j] != 0) {
17                     count++;
18                     sparseArr[count][0] = i;
19                     sparseArr[count][1] = j;
20                     sparseArr[count][2] = chess[i][j];
21                 }
22             }
23         }
24 
25         return sparseArr;
26     }
27     
28     // 恢复稀疏数组.稀疏数组 --> 二维数组
29     public int[][] recover(int[][] sparseArr) {
30         int[][] chessArr = new int[sparseArr[0][0]][sparseArr[0][1]];
31         for (int i = 1; i < sparseArr.length; i++) {
32             chessArr[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
33         }
34 
35         return chessArr;
36     }
37 
38     // 遍历二维数组.得到非 0 数据的个数
39     private int getCount(int[][] chess) {
40         int sum = 0;
41         for (int[] ints : chess) {
42             for (int anInt : ints) {
43                 if (anInt != 0) {
44                     sum++;
45                 }
46             }
47         }
48 
49         return sum;
50     }
51 
52     public void print(int[][] chess) {
53         for (int[] row : chess) {
54             for (int data : row) {
55                 System.out.printf("%d\t", data);
56             }
57             System.out.println();
58         }
59         System.out.println();
60     }
61 
62 }
二维数组、稀疏数组相互转化

  代码示例:测试类

 1 public class Main {
 2     public static void main(String[] args) {
 3         SparseArray sparseArray = new SparseArray();
 4         // 0-没有棋子.1-黑子,2-白子
 5         int[][] chess = new int[4][5];
 6         chess[1][2] = 6;
 7         chess[2][3] = 9;
 8         System.out.println("原始的二维数组~~");
 9         sparseArray.print(chess);
10 
11         // 1.创建稀疏数组
12         final int[][] sparseArr = sparseArray.createSparseArr(chess);
13         System.out.println("得到稀疏数组为~~~~");
14         for (int i = 0; i < sparseArr.length; i++) {
15             System.out.printf("%d\t%d\t%d\t\n", sparseArr[i][0], sparseArr[i][1], sparseArr[i][2]);
16         }
17 
18         // 2.恢复稀疏数组
19         final int[][] chessArr = sparseArray.recover(sparseArr);
20 
21         System.out.println("\n恢复后的二维数组");
22         sparseArray.print(chessArr);
23     }
24 
25 }
测试类

三、单(向)链表

1、介绍

  单链表:每个结点只包含一个指针域。头指针是链表的必要元素。头结点不是。

   带有头结点的单链表:

    不带有头结点的单链表:

  代码示例:单链表结构

1 class Node<E> {
2     public E item;
3     public Node next;
4 
5     public Node(E item) {
6         this.item = item;
7     }
8 }
单链表结构

2、基本操作

  插入:①、s->next = p->next,②、p->next = s

  删除:①、p->next = p->next->next

3、英雄排行榜问题

  代码示例:带头结点的单链表,英雄排行榜实现

  1 // 带有头结点的单链表
  2 public class SingleLinkedList {
  3 
  4     // 头结点
  5     private final Node head;
  6 
  7     public SingleLinkedList() {
  8         // 初始化头结点
  9         head = new Node(0, null);
 10     }
 11 
 12     public Node getHead() {
 13         return head;
 14     }
 15 
 16     /**
 17      * 添加结点到单向链表.直接添加到末尾.rank可重复
 18      *
 19      * @param node
 20      */
 21     public void add(Node node) {
 22         Node temp = head;
 23 
 24         while (true) {
 25             if (temp.next == null) {
 26                 temp.next = node;
 27                 break;
 28             } else {
 29                 temp = temp.next;
 30             }
 31         }
 32     }
 33 
 34     /**
 35      * 添加结点到单向链表.按结点的 rank 属性排名.rank不可重复
 36      *
 37      * @param node
 38      */
 39     public void addByOrder(Node node) {
 40         Node temp = head;
 41 
 42         while (true) {
 43             if (temp.next == null) {
 44                 temp.next = node;
 45                 break;
 46             }
 47 
 48             if (node.rank == temp.next.rank) {
 49                 System.out.println("此排名已存在!添加失败! no = " + node.rank);
 50                 break;
 51             } else if (node.rank < temp.next.rank) {
 52                 /* 插入 */
 53                 node.next = temp.next;
 54                 temp.next = node;
 55                 break;
 56             } else {
 57                 temp = temp.next;
 58             }
 59         }
 60 
 61     }
 62 
 63     /**
 64      * 修改指定的结点
 65      *
 66      * @param node
 67      */
 68     public void update(Node node) {
 69         Node temp = head;
 70 
 71         while (true) {
 72             if (temp.next == null) {
 73                 System.out.println("此排名不存在!修改失败 no = " + node.rank);
 74                 break;
 75             }
 76 
 77             if (temp.next.rank == node.rank) {
 78                 /* 修改 */
 79                 temp.next.name = node.name;
 80                 break;
 81             } else {
 82                 temp = temp.next;
 83             }
 84         }
 85     }
 86 
 87     /**
 88      * 删除指定的结点
 89      *
 90      * @param node
 91      */
 92     public void delete(Node node) {
 93         Node temp = head;
 94 
 95         while (true) {
 96             if (temp.next == null) {
 97                 System.out.println("此排名不存在!删除失败 no = " + node.rank);
 98                 break;
 99             }
100 
101             if (temp.next.rank == node.rank) {
102                 /* 删除 */
103                 temp.next = temp.next.next;
104                 break;
105             } else {
106                 temp = temp.next;
107             }
108         }
109     }
110 
111     /**
112      * 单链表的结点个数
113      */
114     public int size() {
115         Node temp = head;
116 
117         int size = 0;
118         while (temp.next != null) {
119             size++;
120             temp = temp.next;
121         }
122 
123         return size;
124     }
125 
126     /**
127      * 打印链表
128      */
129     public void list() {
130         Node temp = head.next;
131 
132         while (true) {
133             if (temp == null) {
134                 break;
135             } else {
136                 System.out.println(temp);
137                 temp = temp.next;
138             }
139         }
140     }
141 
142 }
143 
144 // 单链表的结点
145 class Node {
146     /**
147      * rank 是 >= 1 的整数
148      */
149     public int rank;
150     public String name;
151     public Node next;
152 
153     public Node(int rank, String name) {
154         this.rank = rank;
155         this.name = name;
156     }
157 
158     @Override
159     public String toString() {
160         return "Node{" +
161                 "rank=" + rank +
162                 ", name='" + name + '\'' +
163                 '}';
164     }
165 }
单链表

  代码示例:测试类

 1 // 测试类
 2 public class Main {
 3     public static void main(String[] args) {
 4         Node node1 = new Node(1, "宋江");
 5         Node node2 = new Node(2, "卢俊义");
 6         Node node3 = new Node(3, "吴用");
 7         Node node4 = new Node(4, "林冲");
 8 
 9         // 头指针
10         SingleLinkedList singleLinkedList = new SingleLinkedList();
11 
12         // 1.直接添加到链表尾部
13         // singleLinkedList.add(node1);
14         // singleLinkedList.add(node2);
15         // singleLinkedList.add(node3);
16         // singleLinkedList.add(node4);
17         //
18         // System.out.println("按顺序直接添加到链表尾部~~");
19         // singleLinkedList.list();
20 
21         // 2.按排序添加到链表
22         singleLinkedList.addByOrder(node1);
23         singleLinkedList.addByOrder(node4);
24         singleLinkedList.addByOrder(node3);
25         singleLinkedList.addByOrder(node2);
26 
27         System.out.println("按排序添加到链表~~");
28         singleLinkedList.list();
29 
30         // 3.更新
31         Node newNode = new Node(4, "小林");
32         System.out.println("更新后链表~~" + "链表长度~" + singleLinkedList.size());
33         singleLinkedList.update(newNode);
34         singleLinkedList.list();
35 
36         // 4.删除
37         singleLinkedList.delete(newNode);
38         System.out.println("删除后链表~~" + "链表长度~" + singleLinkedList.size());
39         singleLinkedList.list();
40     }
41 }
测试类

四、单(向)循环链表

1、介绍

  带有头结点的单循环链表:

  单链表,单循环链表的区别在于空链表的判断。单链表:head->next == null,单循环链表:head->next == head。

2、约瑟夫问题

  问题:n 个结点,从 k(1 <= k <= n)开始报数,数到 m ,求出列序列。

  示例:当 n = 5,k = 1,m = 2 时,出列序列:2,4,1,5,3

  代码示例:不带头结点的单循环链表,约瑟夫问题实现

  1 // 不带头结点的单循环链表
  2 public class SingleCircleLinkedList {
  3 
  4     // 头指针
  5     private Node head;
  6 
  7     // 单循环链表的大小
  8     private int n;
  9 
 10     public SingleCircleLinkedList(int size) {
 11         if (size < 1) {
 12             System.out.println("初始化单循环链表大小size值不正确");
 13             return;
 14         }
 15         this.n = size;
 16 
 17         // 1.创建第一个结点.
 18         head = new Node(1);
 19         head.next = head;
 20 
 21         Node tail = head;
 22         if (size == 1) {
 23             return;
 24         }
 25 
 26         // 2.构建循环链表
 27         for (int i = 2; i <= size; i++) {
 28             final Node node = new Node(i);
 29 
 30             tail.next = node;
 31             node.next = head;
 32 
 33             tail = node;
 34         }
 35     }
 36 
 37     /**
 38      * 约瑟夫环出列
 39      *
 40      * @param k 第 k 个结点开始报数
 41      * @param m 数到 m
 42      */
 43     public void listing(int k, int m) {
 44         // 参数校验
 45         if (head == null || k < 1 || k > n) {
 46             System.out.println("空链表或参数输入有误,请重新输入!");
 47             return;
 48         }
 49 
 50         // 尾指针.(其实这个在构造器中可以拿到).
 51         // 作用:在结点出列的时候用于单链表的删除.
 52         Node tail = head;
 53         while (tail.next != head) {
 54             tail = tail.next;
 55         }
 56 
 57         // 1.第 k 个结点开始报数.前进 k - 1 次
 58         for (int i = 1; i < k; i++) {
 59             head = head.next;
 60             tail = tail.next;
 61         }
 62 
 63         // 2.开始出列
 64         while (head != tail) {
 65             // 3.数到 m.前进 m - 1 次
 66             for (int i = 1; i < m; i++) {
 67                 head = head.next;
 68                 tail = tail.next;
 69             }
 70 
 71             // 4.head指向的结点就是出列的结点
 72             System.out.println(head.no + " 出列");
 73 
 74             // 5.修改指针.
 75             head = head.next;
 76             tail.next = head;
 77         }
 78 
 79         // 6.剩最后一个结点
 80         System.out.println("最后一个结点 " + head.no + " 出列");
 81     }
 82 
 83     public void show() {
 84         if (head == null) {
 85             System.out.println("链表为空~");
 86             return;
 87         }
 88 
 89         // 头指针不动
 90         Node temp = head;
 91 
 92         while (true) {
 93             System.out.println(temp.no);
 94 
 95             if (temp.next == head) {
 96                 break;
 97             }
 98 
 99             temp = temp.next;
100         }
101     }
102 
103     private static class Node {
104         /**
105          * 编号,no >= 1 的整数
106          */
107         public int no;
108 
109         public Node next;
110 
111         public Node(int no) {
112             this.no = no;
113         }
114 
115     }
116     
117 }
约瑟夫问题实现

  代码示例:测试类

 1 // 测试类
 2 public static void main(String[] args) {
 3 
 4     SingleCircleLinkedList singleCircleLinkedList = new SingleCircleLinkedList(5);
 5     // singleCircleLinkedList.show();
 6 
 7     // 出列
 8     singleCircleLinkedList.listing(1, 2);
 9 }
10 
11 // 结果
12 2 出列
13 4 出列
14 1 出列
15 5 出列
16 最后一个结点 3 出列
测试类

五、双(向)链表

1、介绍

  双链表:每个结点包含两个指针域。

  带有头结点的双链表:

  代码示例:双链表结构

1 class Node<E> {
2     public E item;
3     public Node<E> next;
4     public Node<E> prev;
5 
6     public Node(E item) {
7         this.item = item;
8     }
9 }
双链表结构

2、基本操作

  插入:顺序很重要,不能写反了!
    ①、s->next = p->next
    ②、s->prev = p
    ③、p->next->prev = s
    ④、p->next = s

   删除:①、p->prev->next = p->next,②、p->next->prev = p->prev

3、英雄排行榜问题

  代码示例:带头结点的双链表,英雄排行榜实现

  1 // 带有头结点的双链表
  2 public class DoubleLinkedList {
  3 
  4     // 头结点
  5     private final DoubleNode head;
  6 
  7     public DoubleLinkedList() {
  8         // 初始化头结点
  9         head = new DoubleNode(0, null);
 10     }
 11 
 12     public DoubleNode getHead() {
 13         return head;
 14     }
 15 
 16     /**
 17      * 添加结点到双向链表.直接添加到末尾.rank可重复
 18      *
 19      * @param node
 20      */
 21     public void add(DoubleNode node) {
 22         DoubleNode temp = head;
 23 
 24         while (true) {
 25             if (temp.next == null) {
 26                 temp.next = node;
 27                 node.prev = temp;
 28                 break;
 29             } else {
 30                 temp = temp.next;
 31             }
 32         }
 33     }
 34 
 35     /**
 36      * 添加结点到双向链表.按结点的 rank 属性排名.rank不可重复
 37      *
 38      * @param node
 39      */
 40     public void addByOrder(DoubleNode node) {
 41         DoubleNode temp = head;
 42 
 43         while (true) {
 44             if (temp.next == null) {
 45                 temp.next = node;
 46                 node.prev = temp;
 47                 break;
 48             }
 49 
 50             if (node.rank == temp.next.rank) {
 51                 System.out.println("此排名已存在!添加失败! no = " + node.rank);
 52                 break;
 53             } else if (node.rank < temp.next.rank) {
 54                 /* 插入 */
 55                 node.next = temp.next;
 56                 node.prev = temp;
 57                 temp.next.prev = node;
 58                 temp.next = node;
 59                 break;
 60             } else {
 61                 temp = temp.next;
 62             }
 63         }
 64 
 65     }
 66 
 67     /**
 68      * 修改指定的结点
 69      *
 70      * @param node
 71      */
 72     public void update(DoubleNode node) {
 73         DoubleNode temp = head;
 74 
 75         while (true) {
 76             if (temp.next == null) {
 77                 System.out.println("此排名不存在!修改失败 no = " + node.rank);
 78                 break;
 79             }
 80 
 81             if (temp.next.rank == node.rank) {
 82                 /* 修改 */
 83                 temp.next.name = node.name;
 84                 break;
 85             } else {
 86                 temp = temp.next;
 87             }
 88         }
 89     }
 90 
 91     /**
 92      * 删除指定的结点
 93      *
 94      * @param node
 95      */
 96     public void delete(DoubleNode node) {
 97         DoubleNode temp = head.next;
 98 
 99         while (true) {
100             if (temp == null) {
101                 System.out.println("此排名不存在!删除失败 no = " + node.rank);
102                 break;
103             }
104 
105             if (temp.rank == node.rank) {
106                 /* 删除 */
107                 temp.prev.next = temp.next;
108                 if (temp.next != null) {
109                     temp.next.prev = temp.prev;
110                 }
111                 break;
112             } else {
113                 temp = temp.next;
114             }
115         }
116     }
117 
118     /**
119      * 打印链表
120      */
121     public void list() {
122         DoubleNode temp = head.next;
123 
124         while (true) {
125             if (temp == null) {
126                 break;
127             } else {
128                 System.out.println(temp);
129                 temp = temp.next;
130             }
131         }
132     }
133 }
134 
135 /**
136  * 双链表的结点
137  */
138 class DoubleNode {
139     /**
140      * rank 是 >= 1 的整数
141      */
142     public int rank;
143     public String name;
144     public DoubleNode next;
145     public DoubleNode prev;
146 
147     public DoubleNode(int rank, String name) {
148         this.rank = rank;
149         this.name = name;
150     }
151 
152     @Override
153     public String toString() {
154         return "DoubleNode{" +
155                 "rank=" + rank +
156                 ", name='" + name + '\'' +
157                 '}';
158     }
159 }
英雄排行榜实现

  代码示例:测试类

 1 // 测试类
 2 public static void main(String[] args) {
 3     DoubleNode node1 = new DoubleNode(1, "宋江");
 4     DoubleNode node2 = new DoubleNode(2, "卢俊义");
 5     DoubleNode node3 = new DoubleNode(3, "吴用");
 6     DoubleNode node4 = new DoubleNode(4, "林冲");
 7 
 8     // 头指针
 9     DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
10 
11     // 1.直接添加到链表尾部
12     // doubleLinkedList.add(node1);
13     // doubleLinkedList.add(node2);
14     // doubleLinkedList.add(node3);
15     // doubleLinkedList.add(node4);
16 
17     // System.out.println("按顺序直接添加到链表尾部~~");
18     // doubleLinkedList.list();
19 
20     // 2.按排序添加到链表
21     doubleLinkedList.addByOrder(node1);
22     doubleLinkedList.addByOrder(node4);
23     doubleLinkedList.addByOrder(node3);
24     doubleLinkedList.addByOrder(node2);
25 
26     System.out.println("按排序添加到链表~~");
27     doubleLinkedList.list();
28 
29     // 3.更新
30     DoubleNode newNode = new DoubleNode(4, "小林");
31     doubleLinkedList.update(newNode);
32     System.out.println("更新后链表~~");
33     doubleLinkedList.list();
34 
35     // 4.删除
36     doubleLinkedList.delete(newNode);
37     System.out.println("删除后链表~~");
38     doubleLinkedList.list();
39 }
测试类

六、双(向)循环链表

1、介绍

  带有头结点的双循环链表:

2、问题

  要求用双向循环链表实现,用户输入一个数使得26个字母的排列发生变化,例如:输入3,输出结果:DEFGHIJKLMNOPQRSTUVWXYZABC

  同时需要支持负数,例如:输入-3,输出结果:XYZABCDEFGHIJKLMNOPQRSTUVW

  请读者自行实现。

posted @ 2020-12-01 17:49  Craftsman-L  阅读(301)  评论(0编辑  收藏  举报