《数据结构与算法 PHP 语言描述》01 数组

几乎任何一门编程语言都有数组,它是计算机编程中最常见的数据结构。因为数组是编程语言自带的,通常效率很高,能够满足不同需求的数据存储。让我们来学习 PHP 中数组的工作原理,以及使用的场景。

PHP 中数组的定义

首先我们了解下,数组的标准定义:数组,是有序的元素序列,元素可以通过索引来任意存取,索引通常是数字,用来计算元素之间存储位置的偏移量。

PHP 中的数组实际上是一个有序映射。映射是把 values 关联到 keys 的类型。这个类型在很多方面做了优化,因此可以把它当成是真正的数组,或列表(向量),散列表(是映射的一种实现),字典,集合,栈,队列以及更多可能性。另外,数组元素的值也可以是另一个数组,树形结构和多维数组都是可以的。

使用数组

PHP 中的数组很强大。有多种创建和存取元素的方法,也可以通过不同的方式对数组进行查找和排序。同时还提供了一系列函数,方便对数组做各种各样的操作。

创建数组

最简单的方式是使用 array() 或 [] 语言结构来创建一个数组变量:

<?php
$array = array();
$array = [];

使用这种方式创建数组,你将得到单元数量为 0 的空数组。可以通过内建的 count 方法来验证这一点:

$ php -r "echo count(array());"
0

另外,创建数组的时候还可以直接放入一组元素:

<?php
$array = array(1, 2, 3, 4, 5);
$array = [1, 2, 3, 4, 5];

echo count($array); // 5

在 PHP 中,数组中的元素可以是任意类型的值,这个和很多编程语言不同,如下所示:

<?php
$array = [1, 'apple', true, null];

可以使用 is_array 函数来判断一个对下是否是数组,如下所示:

<?php
$number = 3;
$array = [8, 16, 32];
var_dump(is_array($number)); // bool(false)
var_dump(is_array($array)); // bool(true)

我们讨论了创建数组的几种方式,其中从 5.4 起可以使用短数组定义语法,用 [] 代替 array()。

读取数组

在一条赋值语句中,可以使用 [] 操作符将数据赋值给数组,比如下面的循环,将 1~100 的数字赋值给一个数组:

<?php
$numbers = [];

for ($i = 0; $i < 100; $i++) {
    $numbers[$i] = $i + 1;
}

还可使用 [] 操作符读取数组中的元素,如下所示:

<?php
$array = [1, 2, 3, 4, 5];
$sum = $array[0] + $array[1] + $array[2] + $array[3] + $array[4];
echo $sum; // 15

如果要依次读取数组中的所有元素,使用 for 循环会更加简单:

<?php
$numbers = [1, 3, 5, 9, 15, 18, 26];
$sum = 0;

for ($i = 0; $i < count($numbers); $i++) {
    $sum += $numbers[$i];
}

echo $sum; // 77

其中,我们使用 count 函数来控制循环次数,count 可以计算出数组中的单元数目,从而确保循环遍历了数组中的所有元素。

由字符串生成数组

使用字符串函数 explode 可以用一个字符串分隔另一个字符串,该方法通过一些常见的分隔符,比如分隔单词的空格,将一个字符串分成几部分,并将每部分作为一个元素保存到一个新建的数组中。

下面一个小程序演示了 explode 方法的工作原理:

<?php
$sentence = 'Hi zhanbai, I like this book!';
$words = explode(' ', $sentence);

for ($i = 0; $i < count($words); $i++) {
    echo 'word ' . $i . ': ' . $words[$i] . PHP_EOL;
}

输出:

word 0: Hi
word 1: zhanbai,
word 2: I
word 3: like
word 4: this
word 5: book!

另外,str_split 函数也可以将字符串转换为数组,不同的是它是通过设定元素的长度进行分割。preg_split 函数可以通过一个正则表达式分隔字符串为数组。

对数组的整体性操作

有些操作是将数组作为一个整体进行的。首先,可以将一个数组赋给另一个数组:

<?php
$numbers = [];

for ($i = 0; $i < 10; $i++) {
    $numbers[$i] = $i + 1;
}

$same_numbers = $numbers;

当把一个数组赋值给另一个数组时,相当于创建了一个新的数组,当你修改了赋值数组的值,被赋值的数组并不会改变。下面的代码展示了这种情况:

<?php
$numbers = [];

for ($i = 0; $i < 10; $i++) {
    $numbers[$i] = $i + 1;
}

$same_numbers = $numbers;
$numbers[0] = 20;
echo $same_numbers[0]; // 1

如果我们想要被赋值的数组随着原数组的改变而改变,可以使用引用赋值的方式,简单的将一个 & 符号加到要赋值的数组前(源数组):

<?php
$numbers = [];

for ($i = 0; $i < 10; $i++) {
    $numbers[$i] = $i + 1;
}

$same_numbers = &$numbers;
$numbers[0] = 20;
echo $same_numbers[0]; // 20

另一个将数组作为整体操作的是 print_r 函数,用它可以打印数组中的元素,比如:

<?php
$numbers = [1, 2, 3, 4, 5];
print_r($numbers);

输出:

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
)

这样的输出,以易于理解的格式打印数组,当你想要看看一个数组列表时,很方便。

存取函数

PHP 提供了一组用来访问数组元素的函数,这些函数返回目标数组的某种变体。

查找元素

array_search 在数组中搜索给定的值,如果成功则返回首个相应的键名(索引)。下面是一个例子:

<?php
$names = ['zhangsan', 'lisi', 'wangwu', 'lisi', 'zhaoliu'];
fwrite(STDOUT, 'Enter a name to search for: ');
$name = trim(fgets(STDIN));
$position = array_search($name, $names);

if ($position !== false) {
    echo 'Found ' + $name + ' at position ' + $position . PHP_EOL;
} else {
    echo $name . ' not found in array.' . PHP_EOL;
}

执行该程序,并且输入 lisi

$ php a.php
Enter a name to search for: lisi
Found lisi at position 1

如果输入 qianqi

Enter a name to search for: qianqi
qianqi not found in array.

如果数组中包含多个相同的元素,array_search 函数总是返回第一个与参数相同的元素的索引。其它相关的函数有:

  • array_keys — 返回数组中部分的或所有的键名
  • array_key_exists — 检查数组里是否有指定的键名或索引
  • array_key_first — 返回数组中第一个元素的键名或索引
  • array_key_last — 返回数组中最后一个元素的键名或索引

数组的字符串表示

implode 函数可以将一个一维数组的值转化为字符串,各元素之间用自定义的分隔符隔开。比如下面的例子:

<?php
$names = ['zhangsan', 'lisi', 'wangwu', 'lisi', 'zhaoliu'];
$names_str = implode(',', $names);
echo $names_str; // zhangsan,lisi,wangwu,lisi,zhaoliu

其中分隔符 ,是自定义的分隔字符串,其中也可以不填,默认为空字符串。

由已有数组创建新数组

array_mergearray_slice 函数可以通过已有数组创建新的数组。array_merge 可以合并一个或多个数组创建一个新数组,array_slice 可以从数组中取出一段创建一个新数组。

array_merge 的工作原理:将一个或多个数组的单元合并起来,一个数组中的值附加在前一个数组的后面。返回作为结果的数组。如果输入的数组中有相同的字符串键名,则该键名后面的值将覆盖前一个值。然而,如果数组包含数字键名,后面的值将不会覆盖原来的值,而是附加到后面。如果只给了一个数组并且该数组是数字索引的,则键名会以连续方式重新索引。请看下面的例子:

<?php
$names = ['zhangsan', 'lisi', 'wangwu', 'zhaoliu'];
$another_names = ['qianqi', 'sunba', 'lisi', 'zhoujiu'];
$new_names = array_merge($names, $another_names);
print_r($new_names);

输出结果:

Array
(
    [0] => zhangsan
    [1] => lisi
    [2] => wangwu
    [3] => zhaoliu
    [4] => qianqi
    [5] => sunba
    [6] => lisi
    [7] => zhoujiu
)

array_slice 第一个参数是被截取的数组,第二个参数 offset 可以设置偏移量开始的位置,如果为正数则从距离首端这么远开始,为负数则从距离末端这么远开始。第三个参数,决定了元素的数量,为正,则序列中将具有这么多的单元。为负,则序列将终止在距离数组末端这么远的地方。如果省略,则序列将从 offset 开始一直到 array 的末端。 看下面的例子:

<?php
$names = ['zhangsan', 'lisi', 'wangwu', 'zhaoliu'];
$new_names = array_slice($names, 1, 2);
print_r($new_names);

输出:

Array
(
    [0] => lisi
    [1] => wangwu
)

其它相关的函数有:

  • array_merge_recursive — 递归地合并一个或多个数组
  • array_replace — 使用传递的数组替换第一个数组的元素
  • array_combine — 创建一个数组,用一个数组的值作为其键名,另一个数组的值作为其值
  • array_splice — 去掉数组中的某一部分并用其它值取代
  • array_chunk — 将一个数组分割成多个

可变函数

为数组添加元素

有两个方法可以为数组添加元素:array_pusharray_unshiftarray_push 将一个或多个单元压入数组的末尾(入栈):

<?php
$nums = [1, 2, 3, 4, 5];
print_r($nums);

array_push($nums, 6);
print_r($nums);

输出:

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
)
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
    [5] => 6
)

也可以通过数组的长度为数据添加元素:

<?php
$nums = [1, 2, 3, 4, 5];
print_r($nums);

$nums[count($nums)] = 6;
print_r($nums);

输出:

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
)
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
    [5] => 6
)

和在数组的末尾添加元素比起来,在数组的开头添加元素更难。如果不利用数组提供的可变函数,则新的元素添加进来后,需要把后面每个元素都相应的向后移动一个位置,下面代码展示了这一过程:

<?php
$nums = [2, 3, 4, 5];
$new_num = 1;
$length = count($nums);

for ($i = $length; $i >= 1; --$i) {
    $nums[$i] = $nums[$i - 1];
}

$nums[0] = $new_num;
print_r($nums);

输出:

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
)

随着数组中存储的元素越来越多,上述代码将会变得越来越低效。

array_unshift 方法可以将元素添加在数组的开头,下述代码展示该方法的用法:

<?php
$nums = [2, 3, 4, 5];
print_r($nums);

$new_num = 1;

array_unshift($nums, $new_num);
print_r($nums);

$nums = [3, 4, 5];
array_unshift($nums, 1, 2);
print_r($nums);

输出:

Array
(
    [0] => 2
    [1] => 3
    [2] => 4
    [3] => 5
)
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
)
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
)

其中,我们可以看到,可以通过一次调用,为数组添加多个元素。

从数组中删除元素

使用 array_pop 方法,可以弹出数组最后一个单元(出栈):

<?php
$nums = [1, 2, 3, 4, 5, 6];
array_pop($nums);
print_r($nums);

输出:

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
)

如果没有可变函数,从数组中删除第一个元素需要将后续元素各自向前移动一个位置,和在数组开头添加一个元素一样的低效:

<?php
$nums = [8, 1, 2, 3, 4, 5];
print_r($nums);

$length = count($nums);

for ($i = 0; $i < $length; $i++) {
    $nums[$i] = $nums[$i + 1];
}

print_r($nums);

输出:

Array
(
    [0] => 8
    [1] => 1
    [2] => 2
    [3] => 3
    [4] => 4
    [5] => 5
)
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
    [5] =>
)

除了将后续元素前移一位,还多出了一个元素。

array_shift 方法,可以将数组开头的单元移出数组,下述代码展示了该方法的用法:

<?php
$nums = [8, 1, 2, 3, 4, 5];
array_shift($nums);
print_r($nums);

输出:

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
)

返回数组不会多一个元素。array_poparray_shift 都是将删掉的元素作为返回值,因此可以用一个变量来保存删除的元素:

<?php
$nums = [6, 1, 2, 3, 4, 5];
$first = array_shift($nums);
array_push($nums, $first);
print_r($nums);

输出:

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
    [5] => 6
)

从数组中间位置添加和删除元素

删除数组中的第一个元素和在数组开头添加一个元素存在同样的问题,两种操作都需要将数组中剩余的元素向前或者向后移动位置,然而 array_splice 方法可以帮助我们执行其中任意一种操作。

使用 array_splice 方法,需要以下参数:

  • 输入的数组
  • 起始的位置(偏移量)
  • 需要删除的元素的数量
  • 想要添加的数组元素

可以看一个简单的例子,下面程序在数组的中间插入元素:

<?php
$nums = [1, 2, 3, 7, 8, 9];
$new_nums = [4, 5, 6];
array_splice($nums, 3, 0, $new_nums);
print_r($nums);

输出:

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
    [5] => 6
    [6] => 7
    [7] => 8
    [8] => 9
)

如果替换的元素只有一个单元,不需要定义成数组也可以。

下面使用 array_splice 从数组中删除元素:

<?php
$nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
array_splice($nums, 3, 3);
print_r($nums);

输出:

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 7
    [4] => 8
    [5] => 9
)

为数组排序

PHP 有一些用来排序数组的函数,第一个方法 rsort 按照值对数组进行逆向排序(最高到最低),下面展示这个方法的使用:

<?php
$nums = [1, 2, 3, 4, 5];
rsort($nums);
print_r($nums);

输出:

Array
(
    [0] => 5
    [1] => 4
    [2] => 3
    [3] => 2
    [4] => 1
)

sort 方法可以按照值对数组进行正向排序(最低到最高):

<?php
$names = ['apple', 'orange', 'banana', 'pear'];
sort($names);
print_r($names);

输出:

Array
(
    [0] => apple
    [1] => banana
    [2] => orange
    [3] => pear
)

其它还有许多对数组排序的函数,比如:

  • array_multisort — 对多个数组或多维数组进行排序
  • asort — 对数组进行排序并保持索引关系
  • arsort — 对数组进行逆向排序并保持索引关系
  • ksort — 对数组按照键名排序
  • krsort — 对数组按照键名逆向排序
  • natsort — 用“自然排序”算法对数组排序
  • natcasesort — 用“自然排序”算法对数组进行不区分大小写字母的排序
  • shuffle — 打乱数组
  • uasort — 使用用户自定义的比较函数对数组中的值进行排序并保持索引关联
  • uksort — 使用用户自定义的比较函数对数组中的键名进行排序
  • usort — 使用用户自定义的比较函数对数组中的值进行排序

迭代器

可在内部迭代自己的外部迭代器或类的接口。下面例子展示了使用 foreach 时,迭代器方法的调用:

<?php
class myIterator implements Iterator {
    private $position = 0;
    private $array = array(
        'firstelement',
        'secondelement',
        'lastelement',
    );  

    public function __construct() {
        $this->position = 0;
    }

    function rewind() {
        $this->position = 0;
    }

    function current() {
        return $this->array[$this->position];
    }

    function key() {
        return $this->position;
    }

    function next() {
        ++$this->position;
    }

    function valid() {
        return isset($this->array[$this->position]);
    }
}

$it = new myIterator;

foreach($it as $key => $value) {
    var_dump($key, $value);
    echo "\n";
}

输出:

int(0)
string(12) "firstelement"

int(1)
string(13) "secondelement"

int(2)
string(11) "lastelement"

聚合式的迭代器接口,创建外部迭代器的接口。下面例子展示了用法:

<?php
class myData implements IteratorAggregate {
    public $property1 = "Public property one";
    public $property2 = "Public property two";
    public $property3 = "Public property three";

    public function __construct() {
        $this->property4 = "last property";
    }

    public function getIterator() {
        return new ArrayIterator($this);
    }
}

$obj = new myData;

foreach($obj as $key => $value) {
    var_dump($key, $value);
    echo "\n";
}

输出:

string(9) "property1"
string(19) "Public property one"

string(9) "property2"
string(19) "Public property two"

string(9) "property3"
string(21) "Public property three"

string(9) "property4"
string(13) "last property"

二维数组和多维数组

一个数组中的值可以是另一个数组,另一个数组的值也可以是一个数组。依照这种方式,我们可以创建二维或者三维数组。

创建二维数组

二维数组类似一种由行和列构成的数据表格。

在 PHP 中创建二维数组,需要先创建一个数组,然后让数组的每个元素也是一个数组。我们需要知道二维数组要包含多少行,有了这个信息,就可以创建一个 n 行 1 列的二维数组了:

<?php
$twod = [];
$rows = 5;

for ($i = 0; $i < $rows; $i++) {
    $twod[$i] = [];
}

可以仅用一行代码就创建并使用一组初始值来初始化一个二维数组:

<?php
$grades = [[89, 77, 78],[76, 82, 81],[91, 94, 89]];
echo $grades[2][2]; // 显示 89

对于小规模的数据,这是创建二维数组最简单的方式。

处理二维数组的元素

处理二维数组中的元素,有两种最基本的方式:按列访问和按行访问。我们将使用前面创建的数组 $grades 来展示这两种方式的工作原理。

对于两种方式,我们均使用一组嵌入式的 for 循环。对于按列访问,外层循环对应行,内层循环对应列。以数组 $grades 为例,每一行对应一个学生的成绩记录。我们可以将该学生的所有成绩相加,然后除以科目数得到该学生的平均成绩。下面的代码展示了这一过程:

<?php
$grades = [[89, 77, 78],[76, 82, 81],[91, 94, 89]];
$total = 0;
$average = 0;

for ($row = 0; $row < count($grades); $row++) {
    for ($col = 0; $col < count($grades[$row]); $col++) {
        $total += $grades[$row][$col];
    }

    $average = $total / count($grades[$row]);
    echo 'Student ' . ($row + 1) . ' average: ' . round($average, 2) . "\n";
    $total = 0;
    $average = 0;
}

输出:

Student 1 average: 81.33
Student 2 average: 79.67
Student 3 average: 91.33

对于按行访问,只需要稍微调整 for 循环的顺序,使外层循环对应列,内层循环对应行即可。下面的程序计算了一个学生各科的平均成绩:

<?php
$grades = [[89, 77, 78],[76, 82, 81],[91, 94, 89]];
$total = 0;
$average = 0;

for ($col = 0; $col < count($grades); $col++) {
    for ($row = 0; $row < count($grades[$col]); $row++) {
        $total += $grades[$row][$col];
    }

    $average = $total / count($grades[$col]);
    echo 'Student ' . ($col+ 1) . ' average: ' . round($average, 2) . "\n";
    $total = 0;
    $average = 0;
}

输出:

Test 1 average: 85.33
Test 2 average: 84.33
Test 3 average: 82.67

参数不齐的数组

参差不齐的数组是指数组中每行的元素个数彼此不同。很多编程语言在处理这种参差不齐的数组时表现都不是很好,但是 PHP 却表现良好,因为每一行的长度是可以通过计算得到的。

假设数组 $grades 中,每个学生成绩记录的个数是不一样的,不用修改代码,依然可以正确计算出正确的平均分:

<?php
$grades = [[89, 77],[76, 82, 81],[91, 94, 89, 99]];
$total = 0;
$average = 0;

for ($row = 0; $row < count($grades); $row++) {
    for ($col = 0; $col < count($grades[$row]); $col++) {
        $total += $grades[$row][$col];
    }

    $average = $total / count($grades[$row]);
    echo 'Student ' . ($row + 1) . ' average: ' . round($average, 2) . "\n";
    $total = 0;
    $average = 0;
}

输出:

Student 1 average: 83
Student 2 average: 79.67
Student 3 average: 93.25

练习

  1. 创建一个记录学生成绩的对象,提供一个添加成绩的方法,以及一个显示学生平均成绩
    的方法。
  2. 将一组单词存储在一个数组中,并按正序和倒序分别显示这些单词。
  3. 创建这样一个对象,它将字母存储在一个数组中,并且用一个方法可以将字母连在一
    起,显示成一个单词。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343