算法<初级> - 第一章

时间复杂度:

  • Big O

    时间/空间复杂度计算一样,都是跟输入数据源的大小有关

  • n->∞

  • O(logn)

    每次只使用数据源的一半,logn同理

  • 最优解

    先满足时间复杂度的情况最优情况下使用最小空间复杂度

例题:子序列交换

  • 题目描述:

    输入一个序列,如1234567,在5和6位置处分成两个子序列,12345与67,将两个序列交换输出,如该输出为6712345。
  • 思路讲解:
  1. 首先将两个子序列逆序合并
  2. 逆序:左右两指针交换元素值直至中间

    逆序空间复杂度:O(1)
  3. 再将生成的新序列逆序,得到输出结果
  4. 时间复杂度:O(n)
  • 演示:

    • 子逆序合并:

      12345 67

      54321 76 —> 5432176
    • 整体逆序:

      6432175 —> 6732145 —> 6712345
    • 完成√

排序-冒泡排序

  • 思想

    • 序列相邻两元素进行比较
  • 演示(升序)

    • 第一轮:

      确定最大值/最后一位:

      147523689 —> 147523689 —>

      147523689 —> 145723689 —>

      145273689 —> 145237689 —>

      145236789 —> 145236789 —>

      145236789
    • 第二轮:

      确定第二大值/倒数第二位:

      145236789 —> 145236789 —>

      145236789 —> 142536789 —>

      142356789 —> 142356789 —>

      145236789 —>

      145236789
    • 后面同理;
  • 时间复杂度O(n+n-1+n-2...1)=O(n2)

    • 无论好坏
  • 算法实现(Java)

public static void bubbleSort(int[] arr) {  //冒泡排序
if (arr == null || arr.length < 2) {
return;
}
for (int e = arr.length - 1; e > 0; e--) {
for (int i = 0; i < e; i++) {
if (arr[i] > arr[i + 1]) {
swap(arr, i, i + 1);
}
}
}
} public static void swap(int[] arr, int i, int j) { //交换
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}

对数器

验证自我实现算法正确性的方式:与正确算法跑大量同样的测试用例比较结果

  • 随机数发射器

    用于生成大量随机的测试用例
//随机数发射器
public static int[] generateRandomArray(int maxSize, int maxValue) {
//Math.random() -> double [0,1)
//(maxSize + 1) * Math.random() -> [0,size+1] double
//(int) ((maxSize + 1) * Math.random()) -> [0,size] 整数
//生成测试用例数组
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
//随机生成数组中的数(任意方式)
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
}
return arr;
}
  • 用于快速验证贪心策略的正确性
  • 举例:用自我实现的冒泡排序与Java库排序函数进行比较:实现对数器
public static void main(String[] args) {
int testTime = 500000;
int maxSize = 100;
int maxValue = 100;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int[] arr1 = generateRandomArray(maxSize, maxValue);
int[] arr2 = copyArray(arr1);
bubbleSort(arr1);
comparator(arr2);
if (!isEqual(arr1, arr2)) {
succeed = false;
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!"); int[] arr = generateRandomArray(maxSize, maxValue);
printArray(arr);
bubbleSort(arr);
printArray(arr);
} public static void comparator(int[] arr) {
Arrays.sort(arr);
} public static int[] copyArray(int[] arr) {
if (arr == null) {
return null;
}
int[] res = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
res[i] = arr[i];
}
return res;
} // for test
public static boolean isEqual(int[] arr1, int[] arr2) {
if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
return false;
}
if (arr1 == null && arr2 == null) {
return true;
}
if (arr1.length != arr2.length) {
return false;
}
for (int i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
return false;
}
}
return true;
} // for test
public static void printArray(int[] arr) {
if (arr == null) {
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}

排序-选择排序

  • 思想

    • 序列各值与标记位置上的值进行比较
  • 时间复杂度:O(n2)
    • 无论好坏
  • 算法实现(Java)
public static void selectionSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
minIndex = arr[j] < arr[minIndex] ? j : minIndex;
}
swap(arr, i, minIndex);
}
}

排序-插入排序

  • 思想

    • 从第二个(1索引)开始,跟自己之前所有比较,根据结果交换,直至停止,再继续循环
  • 时间复杂度:O(n2)
    • 最好情况:O(n)
  • 算法实现(Java)
public static void insertionSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
swap(arr, j, j + 1);
}
}
}

排序-归并排序

  • 思想

    • 递归

      计算机内栈实现-现场保存变量压栈

      先进后出-保证子递归值返回给父递归

      递归终止条件:base case
    • mergeSort():递归
      • 如果序列len1,或者左右,则返回 (base case)
      • 序列分成左右两个子序列,对两个子序列对其调用归并排序使之有序(递归)

        取中间数方法:(L+R)/2 (L+R)>>1 L+(R-L)/2 —>(防溢出)
    • merge():合并 (又称外排
      • 两个指针子序列头开始,左小放左,右小放右(针对有序序列的排序合并)
      • 当某边为空(越界)时,另一边剩下直接放
  • 演示
    • 74583291 —> 7458 3291 —>

      74 58 32 91 —> 7 4 5 8 3 2 9 1—>

      47 58 23 19 —> 4578 1239
    • 完成√
  • 时间复杂度:O(N* logN)

    T(n)=2T(n/2)+O(N) T(n)递归复杂度 O(N)除递归外复杂度

    • 空间复杂度:O(N)

      • 针对空间复杂度的优化 - 归并排序的内部缓存法 — 空间复杂度O(1)
      • 原地归并排序 — 空间复杂度O(1) 时间复杂度却是O(N2)
    • Master公式:计算递归类算法的时间复杂度公式
      • [补充阅读]
      • T(n)=aT(n/b)+O(Nd)
      • 若logba > d:

        时间复杂度:O(Nlogba)
      • 若logba < d:

        时间复杂度:O(Nd)
      • 若logba = d:

        时间复杂度:O(Nd* logN)
  • 非递归版本思想

    + 每相邻序列之间进行外排
  • 算法实现(Java)
public static void mergeSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
mergeSort(arr, 0, arr.length - 1);
} public static void mergeSort(int[] arr, int l, int r) {
if (l == r) {
return;
}
int mid = l + ((r - l) >> 1);
mergeSort(arr, l, mid);
mergeSort(arr, mid + 1, r);
merge(arr, l, mid, r);
} public static void merge(int[] arr, int l, int m, int r) {
int[] help = new int[r - l + 1];
int i = 0;
int p1 = l;
int p2 = m + 1;
while (p1 <= m && p2 <= r) {
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= m) {
help[i++] = arr[p1++];
}
while (p2 <= r) {
help[i++] = arr[p2++];
}
for (i = 0; i < help.length; i++) {
arr[l + i] = help[i];
}
}

例题:小和问题

  • 题目表述

对于某序列,将各个元素 左边比之小的数相加 ,求和各个元素的如此操作的值,称为小和,返回小和的值。

  • 暴力求解

    遍历-遍历

    时间复杂度:O(n2)
  • 归并排序求解
    • 归并代码
    • 小和产生在外排“左小放左,右小放右”的过程中(左右组间产生小和)
    • 当外排比较时,此时两边都是有序序列
      • 若左小,则右边产生小和,小和值为

        (左值 * 右边未放下序列的个数)
      • 若右小,则照旧进行
res+=arr[p1] < arr[p2] ? (r-p2+1) * arr[p1] : 0 ;
  • 演示

    413506 —> 413 506 —>

    41 3 50 6 —> 4 1 3 5 0 6 —>

    41外排(不产生小和) 3外排 50外排(不产生小和) 6外排 —>

    14 3外排 05 6外排 —>

    • 14 3产生小和:1*3=3
    • 05 6产生小和:01+51=5

    134 056外排产生小和:1 * 2+3 * 2+5 * 2=18

    所以最终小和=3+5+18=26

排序-快速排序

经典快排(partition)

  • 取最后一个数做基准
  • 设一个 小于区 ,边界索引为-1 (大于区同理)
  • 开始遍历序列
    • 若元素小于(等于)基准,则将该元素与 边界索引+1 的位置交换,并将边界索引设置为 交换后该元素位置索引 —>等于是为了将最后位置的基准放到中间区
    • 若元素大于基准,则继续遍历下一个元素
  • 演示
    • 0351674 —> 0351674(边界-1)—>
    • 0351674(边界0)—> 0351674 (边界1) —>
    • 0351674 (边界1) —> 0351674 (边界2) —>
    • 0315674(边界2)—> 0315674 (边界2) —>
    • 0315674(边界3) —> 0314675
  • 算法实现(Java)
public static int[] partition(int[] arr, int l, int r) {
int less = l - 1;
int more = r;
while (l < more) {
if (arr[l] < arr[r]) {
swap(arr, ++less, l++);
} else if (arr[l] > arr[r]) {
swap(arr, --more, l);
} else {
l++;
}
}
swap(arr, more, r);
return new int[] { less + 1, more };
}

例题:荷兰国旗问题

  • 题目表述

    给定一个整数数组,给定一个值K,这个值在原数组中一定存在,要求把数组中小于K的元素放到数组的左边,大于K的元素放到数组的右边,等于K的元素放到数组的中间,最终返回一个整数数组,其中只有两个值,分别是等于K的数组部分的左右两个下标值。
  • 实际上就是一个经典快排的改编(大于区+小于区)
    • 一开始基准归于大于区,大于区判定完后判定小于区,等于不进行操作继续遍历,遍历完后大于区边界与基准交换位置。

随机快排

  • 思想

    • 随机选择序列中一个数,首先将其跟序列最后一个数进行交换,然后对该序列进行一次partition经典快排,返回 小于区边界与大于区边界索引
    • 之后对 (序列左边界 - 小于区边界-1)(大于区边界+1 - 序列右边界) 进行随机快排调用递归
    • 终止条件basecase:L>=R时,返回该序列
  • 时间复杂度
    • 概率长期期望O(NlogN):实际工程非常好,因为排序常数项很少
    • 最优O(NlogN)
    • 最差O(N2):每次随机基准选择都是序列最值
    • 空间复杂度 O(logN):记录每次的排序完后基准位置
  • 算法实现(Java)
public static void quickSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
quickSort(arr, 0, arr.length - 1);
} public static void quickSort(int[] arr, int l, int r) {
if (l < r) {
swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
int[] p = partition(arr, l, r);
quickSort(arr, l, p[0] - 1);
quickSort(arr, p[1] + 1, r);
}
}

排序稳定性

  • 概念

    无序序列排成有序序列后,位置相对次序保持不变 —> 排序算法具有稳定性

  • 可以实现稳定性版本的排序算法

    冒泡排序 - 插入排序 - 归并排序

  • 不可以实现稳定性版本的排序算法

    选择排序 - 快速排序(01stable sort可以实现稳定性)

面试坑题:按奇偶划分左右-次序不变

  • 题目表述

    一个序列,将序列中奇数放在左边,偶数放在右边,且保证奇偶数子序列次序不变,返回新序列,要求额外空间复杂度O(1)

  • 实际上是一个0-1博弈,对一个元素的判定非0即1

  • 大小 / 奇偶 都是0-1博弈的一种判定机制而已

  • 结合题目,实际上就是问实现 稳定性版本快排问题 —— 着重点就是次序不变

  • 要求 01stable sort论文级别算法 - 面试坑题

排序-堆排序

  • 堆结构

    • 堆是一棵完全二叉树(数组可以对应完全二叉树/堆结构)

      • N结点,高度=logN
    • i结点:左孩子结点2i+1 / 右孩子结点2i+2 / 父亲结点(i-1)/2 (地板除)
  • 大根堆:堆每棵子树的头节点都是最大值(小根堆同理)

    • Java中优先队列默认是由小根堆构成,可以用比较器自定义优先级
  • 堆构造大根堆算法:heapinsert()过程

    • 经常用于解其他堆类题目
    1. 遍历每个数
    2. 与其父节点((i-1)/2)进行比较,如果大于父节点,则两个进行交换;交换后再与新父节点进行比较,直至停止(while)
    3. 遍历完后堆就变成了大根堆,小根堆构造同理
    4. 大根堆构造完后,形成上大下小的结构,但是无序
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
} public static void heapInsert(int[] arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
  • 大根堆有序化算法:heapify()过程

    1. 先将大根堆头节点跟最后一个结点交换(最大值固定,size-1)
    2. 遍历除最后结点外所有的节点
    3. 如果小于子节点最大值,则与最大值位置进行交换;交换后再与新子节点进行比较,直至停止(while)
    4. 遍历完后堆就变成了从小到大的有序堆 - 堆排序
int size = arr.length;
swap(arr, 0, --size);
while (size > 0) {
heapify(arr, 0, size);
swap(arr, 0, --size);
} public static void heapify(int[] arr, int index, int size) {
int left = index * 2 + 1;
while (left < size) { //左边不越界
int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left; //找到两个孩子中最大的;如果只有一个左孩子,就返回左孩子
largest = arr[largest] > arr[index] ? largest : index; //孩子最大值与自己比较
if (largest == index) { //自己节点最大
break;
}
swap(arr, largest, index);
index = largest;
left = index * 2 + 1;
}
}
  • 堆排序两个过程

    • 调整堆向上走:heapinsert
    • 调整堆向下走:heapify
    • 堆 - 大根堆 - 有序堆
  • 时间复杂度:
    • 建立大根堆时间复杂度:log1+log2+..logN=O(N)
    • 调整大根堆时间复杂度:O(NlogN)
    • 工程效果不好,原因常数项操作太多

Java_Arrays库 - Arrays.sort()系统排序

  • Arrays.sort(arr)

    • 当arr.size<60左右时,后台使用的是insertion_sort

      原因插入排序常数项很少,在数据项少时使用效果很好
    • 当arr.size>60左右时,后台使用的是merge_sort和quick_sort
      • 当数据类型是int / char / double时,后台用quick_sort
      • 当数据类型是自定义数据类型时,后台默认用merge_sort(可以使用自定义比较器)
      • 两者区别使用的原因:排序稳定性
        • 基础类型不要求稳定性,只要求快(常数项比merge_sort少)
        • 自定义类型要求排序稳定性,现实世界有原始数据次序往下传的需求

比较器

  • 对两个或多个数据项进行比较,以确定它们是否相等,或确定它们之间的大小关系及排列顺序称为比较。 能够实现这种比较功能的电路或装置称为比较器。

  • 定义比较器 / 重新定义比较函数

    • 其中一种继承Comparator接口的比较器实现方法
public static class IdDescendingComparator implements Comparator<Student> {

    @Override    //传入student类
public int compare(Student o1, Student o2) { //比较student类中的id属性
return o2.id - o1.id; //return>0,则第一个参数排序在前;否则第二个参数排序在前
}
} Arrays.sort(students, new IdAscendingComparator());
printStudents(students);

排序-桶排序

  • 与之前的算法不同,排序过程与比较无关,基于数据状况(数据唯一值越少越好)
  • 思想(桶排序是一种思想)
    • 数据序列N个,唯一数据种类n个,准备n个容器
    • 遍历数据,放入对应数据类容器
    • 遍历完后再将容器倒出,排序完成
  • 时间复杂度:O(N),空间复杂度:O(N)
    • 可以用桶排序去减少时间开销,当空间内存富裕的时候
  • 常见的两种桶排序方法
    • 计数排序
    • 基数排序
  • 算法实现(Java)
public static void bucketSort(int[] arr) {       //计数排序实现的桶排序
if (arr == null || arr.length < 2) {
return;
}
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
max = Math.max(max, arr[i]);
}
int[] bucket = new int[max + 1];
for (int i = 0; i < arr.length; i++) {
bucket[arr[i]]++;
}
int i = 0;
for (int j = 0; j < bucket.length; j++) {
while (bucket[j]-- > 0) {
arr[i++] = j;
}
}
}

计数排序

  • 生成0-max索引的数组
  • 遍历序列,遇到某值则数组某值索引++
  • 遍历数组,有多少数则返回多少个对应索引值

例题:最大相邻数差

  • 题目表述

    • 输入一个无序数组,元素long类型,正负0都可以;输出有序后相邻两元素之间的最大差值,要求时间复杂度O(N)
  • 思想

    • 不使用正常排序,想到桶排序,因为时间复杂度O(N);也不适用计数排序,因为元素状况
    • 数组个数N个,准备N+1个桶,将该数组中最小-最大值范围等分成N+1份,对应N+1个桶
    • 将数组元素根据划分范围放入桶中,最小桶和最大桶一定不为空,中间桶一定有一个空桶
    • 核心:
      • 离空桶左右最近的两个桶,左桶的最大值跟右桶的最小值,一定相邻,并且差值一定>=一个桶的范围;所以,我们不用关心一个桶内部相邻情况,只需要知道空桶左右最值相差情况。
    • 所以在数据放入桶的时候,只需要记录桶的最大值/最小值,其他值都不需要存储,遍历完后将找到所有空桶,比较各空桶相邻的两数差即可
  • 演示

    • 3150(-9)8(0.5) 范围(-9 - 8)等分成(7+1)份 有8个桶
    • -9 空 空 空 0,0.5,1 3 5 8
    • 只保留各个桶的最值情况,找到空桶(连续空桶可以跳过),比较空桶相邻两数差
  • 算法过程

    1. boolean min max数组各n+1个元素(建桶)
    2. 序列元素进桶,记录桶号
    3. boolean设置true,比较该桶最大小值
    4. 遍历每一个非空桶,用(上一桶的最大值-本桶的最小值)与全局比较,得到结果
public static int maxGap(int[] nums) {
if (nums == null || nums.length < 2) {
return 0;
}
int len = nums.length;
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i = 0; i < len; i++) {
min = Math.min(min, nums[i]);
max = Math.max(max, nums[i]);
}
if (min == max) {
return 0;
}
boolean[] hasNum = new boolean[len + 1]; //建桶
int[] maxs = new int[len + 1];
int[] mins = new int[len + 1];
int bid = 0;
for (int i = 0; i < len; i++) {
bid = bucket(nums[i], len, min, max); //放桶
mins[bid] = hasNum[bid] ? Math.min(mins[bid], nums[i]) : nums[i]; //更新
maxs[bid] = hasNum[bid] ? Math.max(maxs[bid], nums[i]) : nums[i];
hasNum[bid] = true;
}
int res = 0;
int lastMax = maxs[0];
int i = 1;
for (; i <= len; i++) { //得到结果
if (hasNum[i]) {
res = Math.max(res, mins[i] - lastMax);
lastMax = maxs[i];
}
}
return res;
} public static int bucket(long num, long len, long min, long max) {
return (int) ((num - min) * len / (max - min));
}

算法<初级> - 第一章 排序相关问题的更多相关文章

  1. 算法导论 第一章and第二章(python)

    算法导论 第一章 算法     输入--(算法)-->输出   解决的问题     识别DNA(排序,最长公共子序列,) # 确定一部分用法     互联网快速访问索引     电子商务(数值算 ...

  2. 算法<初级> - 第二章 队列、栈、哈希表相关问题

    算法 - 第二章 数据结构 题目一 用数组实现大小固定的队列和栈(一面题) 数组实现大小固定栈 /*** * size是对头索引(initSize是固定大小) 也是当前栈大小 * size=下个进队i ...

  3. 《算法》第一章部分程序 part 2

    ▶ 书中第一章部分程序,加上自己补充的代码,包括简单的计时器,链表背包迭代器,表达式计算相关 ● 简单的计时器,分别记录墙上时间和 CPU 时间. package package01; import ...

  4. 《算法》第一章部分程序 part 1

    ▶ 书中第一章部分程序,加上自己补充的代码,包括若干种二分搜索,寻找图上连通分量数的两种算法 ● 代码,二分搜索 package package01; import java.util.Arrays; ...

  5. 为什么我要放弃javaScript数据结构与算法(第一章)—— JavaScript简介

    数据结构与算法一直是我算比较薄弱的地方,希望通过阅读<javaScript数据结构与算法>可以有所改变,我相信接下来的记录不单单对于我自己有帮助,也可以帮助到一些这方面的小白,接下来让我们 ...

  6. 啊哈!算法(第一章)C#实现

    第1节 最简单的排序--桶排序     期末考试完了老师要将同学们的分数按照从高到低排序. 小哼的班上只有 5 个同学,这 5 个同学分别考了 5 分.3 分.5 分.2 分和 8 分,考得真是惨不忍 ...

  7. 算法图解第一章_二分查找_python

    什么是二分查找? 我们先玩一个游戏. 在1至100之间我写下一个数,由你来猜测这个数是多少.我会告诉你高了还是低了. 最简单的办法就是每次取一半. 例如 "50""低了& ...

  8. 第23章 排序算法(包括merge等)

      第23章 排序算法  Sorting:1 sort Sort elements in range (function template)2 stable_sort Sort elements pr ...

  9. Python3-Cookbook总结 - 第一章:数据结构和算法

    第一章:数据结构和算法 Python 提供了大量的内置数据结构,包括列表,集合以及字典.大多数情况下使用这些数据结构是很简单的. 但是,我们也会经常碰到到诸如查询,排序和过滤等等这些普遍存在的问题. ...

随机推荐

  1. JavaScript之深入函数(二)

    上一篇我们主要讲解了函数的执行过程和原理,本篇我们将介绍函数的另外两个特殊表现:闭包和立即执行函数. 一 闭包 1,  闭包的形成 之前我们提到,函数执行完毕,马上就会销毁自己的AO对象.但是如果遇到 ...

  2. css超出多行隐藏

      单行隐藏: overflow: hidden;/*超出部分隐藏*/ text-overflow:ellipsis;/* 超出部分显示省略号 */ white-space: nowrap;/*规定段 ...

  3. WordPress对接微信小程序遇到的问题

    1.文章内容中的“<”和“>”字符显示问题 小程序是使用“wxPares工具来实现html转wxml的,如果你的文本包含了代码比如xml会携带<>符号,程序会将其转化,造成解析 ...

  4. JVM 调优 - jstat

    Java命令学习系列(四)——jstat 2015-07-31 分类:Java 阅读(11041) 评论(1) 阿里大牛珍藏架构资料,点击链接免费获取 jstat(JVM Statistics Mon ...

  5. Asteroids POJ - 3041 二分图最小点覆盖

       Asteroids POJ - 3041 Bessie wants to navigate her spaceship through a dangerous asteroid field in ...

  6. github- 优秀资源总结

    权限控制篇: SpringMVC-Mybatis-Shiro-redis-0.2-master:https://www.sojson.com/shiro / https://m.imooc.com/a ...

  7. 如何解决myeclipse2014突然无法打开的问题

    今天突然发现我的myeclipse2014打开不了,昨晚还好好的,上网找了一下没有找到解决方法,于是新建一个工作区间Workspace Test,再打开File->Switch WorkSpac ...

  8. python 列表,集合,字典,字符串的使用

    PY PY基础 append 在末尾添加元素 pop 删除末尾元素 insert(i,item)在i位插入元素 pop(i)删除i位元素 只有1个元素的tuple定义时必须加一个逗号,,来消除歧义 i ...

  9. WebGL简易教程(九):综合实例:地形的绘制

    目录 1. 概述 2. 实例 2.1. TerrainViewer.html 2.2. TerrainViewer.js 3. 结果 4. 参考 1. 概述 在上一篇教程<WebGL简易教程(八 ...

  10. Docker4-docker私库的搭建及常用方法-docker-registry方式

    一.简单介绍 前面已经介绍,可以使用Docker Hub公共仓库,但是大多数情况企业都需要创建一个本地仓库供自己使用.这里介绍几种搭建私库的方法 私库的好处有几点 1.节约带宽 2.可以自己定制系统 ...