javascript数据结构和算法 第二章 (数组) 二
字符串表示的数组
join() 和 toString() 函数返回数组的字符串表示.这两个函数通过将数组中的元素用逗号分隔符切割,返回字符串数组表示.
这里有个样例:
var names = ["David", "Cynthia", "Raymond", "Clayton", "Mike", "Jennifer"];
var namestr = names.join();
print(namestr); // David,Cynthia,Raymond,Clayton,Mike,Jennifer
namestr = names.toString();
print(namestr);
//打印 David,Cynthia,Raymond,Clayton,Mike,Jennifer
当你调用print()函数来打印数组时,它自己主动的为数组调用toString()函数.
print(names);
//打印 David,Cynthia,Raymond,Clayton,Mike,Jennifer
从已有数组中创建一个新的数组
concat()和splice()两个函数同意你从一个已有的数组创建一个新的数组.
concat()函数同意你将两个或者多个数组连接创建一个新的数组.
splice()函数同意你从一个已有数组创建一个子数组.
以下我们来看看concat()函数时怎样工作的.
一个已有的数组调用concat()函数,而且传入另外一个数组作为參数.
这个參数将会连接到数组的后面.以下的程序显示concat():
var cisDept = ["Mike", "Clayton", "Terrill", "Danny", "Jennifer"];
var dmpDept = ["Raymond", "Cynthia", "Bryan"];
var itDiv = cisDept.concat(dmpDept);
print(itDiv);
itDiv = dmp.concat(cisDept);
print(itDiv);
程序将会输出:
Mike,Clayton,Terrill,Danny,Jennifer,Raymond,Cynthia,Bryan
Raymond,Cynthia,Bryan,Mike,Clayton,Terrill,Danny,Jennifer
第一行的输出显示是cisDept数组先输出.
第二行的输出显示是dmpDept数组先输出.
splice()函数从已有数组的内容中创建一个新的数组.
传递给数组的參数是開始切割的数组位置和获取数组元素的长度.
var itDiv = ["Mike","Clayton","Terrill","Raymond","Cynthia","Danny","Jennifer"]; var dmpDept = itDiv.splice(3,3);
var cisDept = itDiv;
print(dmpDept); // Raymond,Cynthia,Danny
print(cisDept); // Mike,Clayton,Terrill,Jennifer
slice()函数还有其它的用处,比如向数组中加入或者删除元素.
赋值函数
javascript提供一系列赋值函数,它们同意你不使用引用单个元素来改动数组内容.这些函数使复杂的赋值技术变得简单.
向数组中加入元素
push()和shift()函数能够用来向数组中加入元素.
push()函数用来向数组的尾部加入元素:
var nums = [1,2,3,4,5];
print(nums); // 1,2,3,4,5
nums.push(6);
print(nums); // 1,2,3,4,5,6
使用push()函数比使用length属性加入更加直观.
var nums = [1,2,3,4,5];
print(nums); // 1,2,3,4,5
nums[nums.length] = 6;
print(nums); // 1,2,3,4,5,6
向数组的首部比向数组的尾部加入数组更加困难.
假设不使用赋值函数的处理的话.那么数组中每一个已知的元素都须要在新数据插入之前向后移动一位.
以下的代码来解释这个问题.
var nums = [2,3,4,5];
var newnum = 1;
var N = nums.length;
for (var i = N; i >= 0; --i) {
nums[i] = nums[i-1];
}
nums[0] = newnum;
print(nums); // 1,2,3,4,5
假设数组中存储数据的个数增长时这段代码将会越来越低效.
向数组的首部加入元素的赋值函数是unshift();
var nums = [2,3,4,5];
print(nums); // 2,3,4,5
var newnum = 1;
nums.unshift(newnum);
print(nums); // 1,2,3,4,5
nums = [3,4,5];
nums.unshift(newnum,1,2);
print(nums); // 1,2,3,4,5
你能够一次向函数传递多个元素来向数组首部加入元素.
移除数组中的元素
从数组的尾部移除数据能够简单的使用pop()赋值函数:
var nums = [1,2,3,4,5,9];
nums.pop();
print(nums); // 1,2,3,4,5
在不使用赋值语句的情况下,从数组的首部移除元素须要将数组的全部元素向前移动,这时会出现刚才向数组首部加入元素一样的低效.
var nums = [9,1,2,3,4,5];
print(nums);
for (var i = 0; i < nums.length; ++i) {
nums[i] = nums[i+1];
}
print(nums); // 1,2,3,4,5,
除了我们须要左移动数组元素以外的事实,我们还留下了一个多余的的元素.
当我们打印数组的时候,我们发现了在数组的尾部出现了一个多余的逗号.
从数组首部移除元素的赋值函数是shift().
var nums = [9,1,2,3,4,5];
nums.shift();
print(nums); // 1,2,3,4,5
你会发如今数组的尾部并没有出现多余的元素.
pop()函数和shift()函数都将会返回被移除的元素,所以你能够将它们存储到一个变量中.
var nums = [6,1,2,3,4,5];
var first = nums.shift(); // first gets the value 6
nums.push(first);
print(nums); // 1,2,3,4,5,6
向数组的中间部位加入或者删除元素
slice()函数能够用来向数组中加入或者删除元素.
使用slice()向数组中加入元素,你须要提供下面的參数.
開始下标(你想要插入元素的起始位置)
须要移除的元素个数(0表示你要加入元素)
你想要加入的元素
以下我们来看一个向数组中间加入元素的样例.
var nums = [1,2,3,7,8,9];
var newElements = [4,5,6];
nums.splice(3,0,newElements);
print(nums); // 1,2,3,4,5,6,7,8,9
插入数组的元素并不一定要是一个有名数组,它能够是不论什么形式的列表.
var nums = [1,2,3,7,8,9];
nums.splice(3,0,4,5,6);
print(nums);
在上面的样例中,4,5,6就代表着我们要插入到nums中的列表元素.
以下是一个使用splice()函数从数组中移除元素的样例.
var nums = [1,2,3,100,200,300,400,4,5];
nums.splice(3,4);
print(nums); // 1,2,3,4,5
给数组元素排序
最后两个赋值函数被用来对数组元素进行某种形式的排序.
第一个函数时reverse().将数组中的元素进行翻转.
var nums = [1,2,3,4,5];
nums.reverse();
print(nums); // 5,4,3,2,1
我们常常须要对数组中的元素进行排序.完毕这个任务的赋值函数时sort(),它可以非常好的处理字符串.
var names = ["David","Mike","Cynthia","Clayton","Bryan","Raymond"];
nums.sort();
print(nums); // Bryan,Clayton,Cynthia,David,Mike,Raymond
可是sort()却不能非常好的处理数字.
var nums = [3,1,2,100,4,200];
nums.sort();
print(nums); // 1,100,2,200,3,4
sort()函数像字典一样,假定数组元素是字符串,由于在先前的的样例中,数组元素是数字.为了让其正确的对数字进行排序.我们能够向其传递一个排序函数作为第一个參数,.sort()函数将会用它来比較数组的元素对来决定它们的正确顺序.
对于数字来说,排序函数能够简单的将一个数减去还有一个数.假设返回值是负数,表示左边的操作数小于右边的操作数,假设等于0,表示它们相等,假设大于0表示左边的操作数大于右边的操作数.
基于这一点,让我们又一次执行之前的小程序.
.function compare(num1, num2) {
return num1 - num2;
}
var nums = [3,1,2,100,4,200];
nums.sort(compare);
print(nums); // 1,2,3,4,100,200
sort()函数使用compare()来数字化而不是字典化的排序数组.
迭代函数
我们要讲的最后一部分函数是迭代函数.
这些函数能够为每一个数组元素运行一个操作,要么返回一个值,一个值的集合,或者是一个新的数组.
不生成新数组的迭代函数
我们首先要讨论的是不生成新数组迭代函数,取而代之,它们要么对数组的每一个元素运行某些操作,要么生成一个简单的值.
第一个迭代函数是forEach().这个函数使用一个函数作为參数而且使每一个数组元素调用该函数.
function square(num) {
print(num, num * num);
}
var nums = [1,2,3,4,5,6,7,8,9,10];
nums.forEach(square);
函数的输出是:
1 1
2 4
3 9
4 16
5 25
6 36
7 49
8 64
9 81
10 100
接下来的一个函数时every().向数组传递一个返回值为boolean类型的函数.
假设数组中的每一个元素调用參数函数时都返回true,函数返回true.
function isEven(num) {
return num % 2 == 0;
}
var nums = [2,4,6,8,10];
var even = nums.every(isEven);
if (even) {
print("all numbers are even");
} else {
print("not all numbers are even");
}
返回的结果是:
all numbers are even
假设我们将数组改为:
var nums = [2,4,6,7,8,10];
那么程序将会输出:
not all numbers are even
some()函数相同带一个Boolean函数作为參数.而且在数组中至少有一个元素在调用參数函数之后返回true时返回true.
function isEven(num) {
return num % 2 == 0;
}
var nums = [1,2,3,4,5,6,7,8,9,10];
var someEven = nums.some(isEven);
if (someEven) {
print("some numbers are even");
} else {
print("no numbers are even");
}
nums = [1,3,5,7,9];
someEven = nums.some(isEven);
if (someEven) {
print("some numbers are even");
} else {
print("no numbers are even");
}
函数的结果是:
some numbers are even
no numbers are even
reduce()函数使用一个累加器作为參数,而且连续的计算直到数组结束.返回一个单值.
以下是一个使用reduce()对数组元素进行总计.
function add(runningTotal, currentValue) {
return runningTotal + currentValue;
}
var nums = [1,2,3,4,5,6,7,8,9,10];
var sum = nums.reduce(add);
print(sum); // 打印55
reduce()函数结合add()函数,从左至右,计算数组元素的总和.
add(1,2) -> 3
add(3,3) -> 6
add(6,4) -> 10
add(10,5) -> 15
add(15,6) -> 21
add(21,7) -> 28
add(28,8) -> 36
add(36,9) -> 45
add(45,10) -> 55
我们相同能够使用reduce()来连接数组.
function concat(accumulatedString,item){
return accumulatedString+item;
}
var words = [‘the’,’quick’,’brown’,’fox’];
var sentence = words.reduce(concat);
print(sentence);//打印“the quick brown fox”
javascript还提供了一个reduceRight()函数,它很类似于reduce()函数,仅仅是它是从右边往左边计算.以下的程序使用reduceRight()来对数组元素进行翻转操作.
function concat(accumulatedString,item){
return accumulatedString+item;
}
var words = [‘the’,’quick’,’brown’,’fox’];
var sentence = words.reduceRight(concat);
print(sentence); // displays "fox brown quick the"
返回新数组的迭代函数
map()和filter()是两个返回新数组的迭代函数.
map()类似于forEach()函数,为数组的每个元素调用一个函数.
不同点是map()会返回调用函数之后的生成的新数组.
function curve(grade){
return grade += 5;
}
var grades = [77,65,81,92,83];
var newgrades = grades.map(curve);
print(newgrades);//82,70,86,97,88
以下是使用字符串的样例:
function first(word){
return word[0];
}
var words = [‘for’,’your’,’information’];
var acronym = words.map(first);
print(acronym.join(“”));//display ‘fyi’
在这个样例中,acronym数组保存了words数组中每一个元素字符串的第一个字母.
然而,假设我们想展现数组中每一个元素的首字母缩略词,我们须要调用toString()函数来使用逗号分隔数组的元素.
这里我们使用join()函数而且使用一个空字符串作为其分隔符.
filter()函数类似于every(),可是并不像every()那样返回一个Boolean值,它返回那些在调用给定函数之后返回true的那些数组元素.
function isEven(num){
return num % 2 == 0;
}
function isOdd(num){
return num % 2 !=0;
}
var nums = [];
for(var i=0;i<20; ++i){
num[i] = i+1;
}
var evens = nums.filter(isEven);
print(“Even numbers:”);
print(evens);
var odds = num.filter(isOdd);
print(“Odd numbers:”);
print(odds);
程序终于输出:
Even numbers:
2,4,6,8,10,12,14,16,18,20
Odd numbers:
1,3,5,7,9,11,13,15,17,19
这里还有还有一个使用filter()的有趣的样例.
function passing(num) {
return num >= 60;
}
var grades = [];
for (var i = 0; i < 20; ++i) {
grades[i] = Math.floor(Math.random() * 101);
}
var passGrades = grades.filter(passing);
print("All grades: );
print(grades);
print("Passing grades: ");
print(passGrades);
程序输出:
All grades:
39,43,89,19,46,54,48,5,13,31,27,95,62,64,35,75,79,88,73,74
Passing grades: 89,95,62,64,75,79,88,73,74
当然,我们的filter能够使用在字符串上.
function after(c){
if(c.indexOf(‘cie’)>-1){
return true;
}
return false;
}
var words = ["recieve","deceive","percieve","deceit","concieve"];
var misspelled = words.filter(afterc);
print(misspelled); // displays recieve,percieve,concieve
二维数组和多维数组
javascript数组是一维的,可是你能够通过数组的数组这样的形式来创建多维数组.
在这部分,我们将会讨论怎样创建二维数组.
创建二维数组
二维数组的结构类似于电子表格那样带有行和列.
为了在javascript中创建二维数组,我们须要首先创建一个数组,然后使数组中的每一个元素相同是一个数组.
至少,我们须要知道我们这个数组包括的行数.有了这个信息,我们就能够创建一个n行1列的二维数组了.
var twod = [];
var rows = 5;
for(var i=0;i<rows;++i){
var twod[i] = [];
}
这种方法的问题在于数组的每一个元素都被设置为undefined.
创建二维数组的更好的方式就是遵循Javascript:Good Parts.
Array.matrix = function(numrows , numcols , initial){
var arr = [];
for(var i=0; i < numrows ; ++i){
var columns = [];
for(var j = 0; j < numcols; ++j){
columns[j] = initial;
}
arr[i] = columns;
}
return arr;
}
var nums = Array.matrix(5,5,0);
print(nums[1][1]); // displays 0
var names = Array.matrix(3,3,"");
names[1][2] = "Joe";
print(names[1][2]); // display "Joe"
相同,我们能够创建一个二维数组而且在一行里初始化它们.
var grades = [[89,77,78],[76,82,81],[91,94,89]];
print(grades[2][2]);
对于小的数据集合,这样的方法是创建一个二维数组最方便的方法.
加工二维数组的元素
加工一个二维数组有两种主要的模式.一种模式强调一列一列的获取.而还有一种模式则强调从行获取元素.我们将会使用前面写过的代码片段grades数组 来演示这些模式的工作方式.
对于两种模式来说,我们都须要设置一系列嵌套的循环.,对于列获取模式来说,我们的外部循环在每行移动,而内部循环这是在列上移动.
对于grades数组,考虑到每一行代表着一个学生的一系列成绩
我们能够通过总计每一行的数值然后除以该行全部的元素个数算出每一个学生的成绩.以下的代码展现的就是这个过程.
var grades = [[89, 77, 78],[76, 82, 81],[91, 94, 89]];
var total = 0;
var average = 0;
for(var row=0;row < grades.length;++row){
for(var col = 0; col < grades[row].length ; ++col){
total += grades[row][col];
}
average = total /grades[row].length;
print(‘Student “ + parseInt(row+1)+ “ average: “ + average.toFixed(2));
total = 0;
average = 0.0;
}
内部循环被表达式:
col < grades[row].length;
控制.
这个之所以能正常工作是由于每一行都包括了一个数组,而且我们能够通过数组的length属性来得知数组每行有多少列.
每一个学生成绩的平均值都使用toFixed(n)精确到2位.
Student 1 average: 81.33
Student 2 average: 79.67
Student 3 average: 91.33
为了展现按列计算.我们仅仅须要简单的移动for循环使外部循环控制列,内部循环控制行.
var grades = [[89, 77, 78],[76, 82, 81],[91, 94, 89]];
var total = 0; var average = 0.0;
for (var col = 0; col < grades.length; ++col) {
for (var row = 0; row < grades[col].length; ++row) {
total += grades[row][col];
}
average = total / grades[col].length;
print("Test " + parseInt(col+1) + " average: " + average.toFixed(2));
total = 0; average = 0.0;
}
程序的输出是:
Test 1 average: 85.33
Test 2 average: 84.33
Test 3 average: 82.67
參差不齐的数组
參差不齐的数组是指数组的每一行可能有不同数量的元素.一行可能有三个元素同一时候还有一行可能仅仅有一个元素.很多程序语言在处理这些參差不齐的数组时都会感到棘手.可是javascript 确不会.由于我们可以计算数组随意一行的长度.
举个样例.学生的成绩数组中学生拥有不同数量的成绩记录.我们甚至不须要改动程序就行计算出每一个学生成绩的平均值.
var grades = [[89, 77],[76, 82, 81],[91, 94, 89, 99]];
var total = 0; var average = 0.0;
for (var row = 0; row < grades.length; ++row) {
for (var col = 0; col < grades[row].length; ++col) {
total += grades[row][col];
}
average = total / grades[row].length;
print("Student " +parseInt(row+1) + " average: " + average.toFixed(2));
total = 0;
average = 0.0;
}
注意第一个学生有两个成绩,第二个学生有三个成绩,而第四个学生却有4个成绩.由于成绩计算每一列的长度,所以层次不齐的数组依旧不会影响到程序.
以下是程序输出的结果:
Student 1 average: 83.00
Student 2 average: 79.67
Student 3 average: 93.25
数组对象
在这一章的全部样例中,数组的元素都是主要的数据类型,如数字和字符串.
数组相同能够包括对象,而且数组原有的属相和方法相同有效.
function Point(x,y) {
this.x = x; this.y = y;
}
function displayPts(arr) {
for (var i = 0; i < arr.length; ++i) {
print(arr[i].x + ", " + arr[i].y);
}
}
var p1 = new Point(1,2);
var p2 = new Point(3,5);
var p3 = new Point(2,8);
var p4 = new Point(4,4);
var points = [p1,p2,p3,p4];
for (var i = 0; i < points.length; ++i) {
print("Point " + parseInt(i+1) + ": " + points[i].x + ", " +points[i].y);
}
var p5 = new Point(12,-3);
points.push(p5);
print("After push: ");
displayPts(points);
points.shift();
print("After shift: ");
displayPts(points);
程序输出为:
Point 1: 1, 2
Point 2: 3, 5
Point 3: 2, 8
Point 4: 4, 4
After push:
1, 2
3, 5
2, 8
4, 4
12, -3
After shift:
3, 5
2, 8
4, 4
12, -3
point 12,-3 通过使用push()增加到数组中.point 1,2通过使用shift()移出数组.
对象中的数组
我们能够在对象中使用数组存储复杂的数据.我们在这本书中使用的很多数据结构都是通过实现一个对象,而且通过使用对象中的数组来存储数据的.
以下是该书使用的一些技巧.
function weekTemps() {
this.dataStore = [];
this.add = add;
this.average = average;
}
function add(temp) {
this.dataStore.push(temp);
}
function average() {
var total = 0;
for (var i = 0; i < this.dataStore.length; ++i) {
total += this.dataStore[i];
}
return total / this.dataStore.length;
}
var thisWeek = new weekTemps();
thisWeek.add(52);
thisWeek.add(55);
thisWeek.add(61);
thisWeek.add(65);
thisWeek.add(55);
thisWeek.add(50);
thisWeek.add(52);
thisWeek.add(49);
print(thisWeek.average()); // displays 54.875
你会发现add()函数使用push()函数将数据压入dataStore数组中.在对象中使用直观的函数名称是一个通用的技巧.
不是每一个人都知道向数组中压入一个数据是什么意思,可是他们一定知道向数组加入一个数据是什么意思.
昨天晚上我们站庆,竟然喝多了...如今还有点头疼...明天还有考试...不说了...
Sharing happiness
Best wishes.
javascript数据结构和算法 第二章 (数组) 二的更多相关文章
- java版数据结构与算法第二章数组
数组由一组具有相同类型的数据元素组成,并存储在一组连续存储单元中.一维数组是常量. 二维数组:若一维数组中的数据元素又是一堆数据结构,我们称之为二维数组.二维数组可以看成是n个列向量组成的线性表. 数 ...
- 【学习总结】java数据结构和算法-第二章-数据结构和算法概述
总目录链接 [学习总结]尚硅谷2019java数据结构和算法 github:javaDSA 目录 数据结构和算法的关系 几个实际编程中的问题 线性结构和非线性结构 数据结构和算法的关系 几个实际编程中 ...
- 为什么我要放弃javaScript数据结构与算法(第二章)—— 数组
第二章 数组 几乎所有的编程语言都原生支持数组类型,因为数组是最简单的内存数据结构.JavaScript里也有数组类型,虽然它的第一个版本并没有支持数组.本章将深入学习数组数据结构和它的能力. 为什么 ...
- 重读《学习JavaScript数据结构与算法-第三版》- 第3章 数组(一)
定场诗 大将生来胆气豪,腰横秋水雁翎刀. 风吹鼍鼓山河动,电闪旌旗日月高. 天上麒麟原有种,穴中蝼蚁岂能逃. 太平待诏归来日,朕与先生解战袍. 此处应该有掌声... 前言 读<学习JavaScr ...
- 重读《学习JavaScript数据结构与算法-第三版》- 第6章 链表(一)
定场诗 伤情最是晚凉天,憔悴厮人不堪言: 邀酒摧肠三杯醉.寻香惊梦五更寒. 钗头凤斜卿有泪,荼蘼花了我无缘: 小楼寂寞新雨月.也难如钩也难圆. 前言 本章为重读<学习JavaScript数据结构 ...
- 为什么我要放弃javaScript数据结构与算法(第十一章)—— 算法模式
本章将会学习递归.动态规划和贪心算法. 第十一章 算法模式 递归 递归是一种解决问题的方法,它解决问题的各个小部分,直到解决最初的大问题.递归通常涉及函数调用自身. 递归函数是像下面能够直接调用自身的 ...
- 为什么我要放弃javaScript数据结构与算法(第九章)—— 图
本章中,将学习另外一种非线性数据结构--图.这是学习的最后一种数据结构,后面将学习排序和搜索算法. 第九章 图 图的相关术语 图是网络结构的抽象模型.图是一组由边连接的节点(或顶点).学习图是重要的, ...
- 为什么我要放弃javaScript数据结构与算法(第六章)—— 集合
前面已经学习了数组(列表).栈.队列和链表等顺序数据结构.这一章,我们要学习集合,这是一种不允许值重复的顺序数据结构. 本章可以学习到,如何添加和移除值,如何搜索值是否存在,也可以学习如何进行并集.交 ...
- 为什么我要放弃javaScript数据结构与算法(第五章)—— 链表
这一章你将会学会如何实现和使用链表这种动态的数据结构,这意味着我们可以从中任意添加或移除项,它会按需进行扩张. 本章内容 链表数据结构 向链表添加元素 从链表移除元素 使用 LinkedList 类 ...
随机推荐
- 洛谷—— P2562 [AHOI2002]Kitty猫基因编码
P2562 [AHOI2002]Kitty猫基因编码 题目描述 小可可选修了基础生物基因学.教授告诉大家 Super Samuel 星球上 Kitty猫的基因的长度都是 2 的正整数次幂 ), 全是由 ...
- Xamarin XAML语言教程模板视图TemplatedView(二)
Xamarin XAML语言教程模板视图TemplatedView(二) (2)打开MainPage.xaml文件,编写代码,将构建的控件模板应用于中TemplatedView.代码如下: <? ...
- Python的zip函数(转)
原文地址:http://www.cnblogs.com/frydsh/archive/2012/07/10/2585370.html zip函数接受任意多个(包括0个和1个)序列作为参数,返回一个tu ...
- 关于SOA
什么是SOA SOA:面向服务的体系结构(Service-Oriented Architecture,SOA,也叫面向服务架构), SOA是指为了解决在Internet环境下业务集成的需要,通过连接能 ...
- Codeforces 538 F. A Heap of Heaps
\(>Codeforces \space 538 F. A Heap of Heaps<\) 题目大意 :给出 \(n\) 个点,编号为 \(1 - n\) ,每个点有点权,将这些点构建成 ...
- 【混合背包】CDOJ1606 难喝的饮料
#include<cstdio> #include<algorithm> using namespace std; int n,V,op[20010],c[20010],w[2 ...
- 【拓扑排序】CDOJ1635 琵琶弦上说相思,当时明月在,曾照彩云归
对于两个相邻的字符串 Si和Si+1 ,如果它们的前k-1位都相同,第k位不相同,那么,在字典序中 Si,k一定在 Si+1,k前面 建立有向边从 Si,k到 Si+1,k,进行拓扑排序 为了保证字典 ...
- 星际争霸 虚空之遗 人族5BB 操作流程
人族5BB rush timing 3min-3min30 一波战术,对面双开不侦察应该就GG了. 14农民BS,建议在第一个BS后的100矿,马上接上一个BS堵口,基本上对面是侦察不到的,特别是内双 ...
- Codeforces Gym 100269G Garage 数学
Garage 题目连接: http://codeforces.com/gym/100269/attachments Description Wow! What a lucky day! Your co ...
- fedora19/opensuse13.1 配置openvpn client
Date: 20140207Auth: Jin 1.install # yum -y install openvpn #zypper install openvpn 2.copy user key # ...