shu暑期研究项目-——排序研究

#include <iostream>
#include <vector>
#include <chrono>
#include <random>
#include <ctime>
#include <algorithm>
using namespace std;
using namespace chrono;

// ============ 1. Bubble Sort ============
void bubbleSort(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; i++) {
        bool swapped = false;
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr[j], arr[j + 1]);
                swapped = true;
            }
        }
        if (!swapped) break;
    }
}

// ============ 2. Selection Sort ============
void selectionSort(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; i++) {
        int minIdx = i;
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIdx]) {
                minIdx = j;
            }
        }
        if (minIdx != i) {
            swap(arr[i], arr[minIdx]);
        }
    }
}

// ============ 3. Quick Sort ============
int partition(vector<int>& arr, int low, int high) {
    int pivot = arr[high];
    int i = low - 1;
    for (int j = low; j < high; j++) {
        if (arr[j] <= pivot) {
            i++;
            swap(arr[i], arr[j]);
        }
    }
    swap(arr[i + 1], arr[high]);
    return i + 1;
}

void quickSort(vector<int>& arr, int low, int high) {
    if (low < high) {
        int pi = partition(arr, low, high);
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    }
}

void quickSortWrapper(vector<int>& arr) {
    quickSort(arr, 0, arr.size() - 1);
}

// ============ 4. Merge Sort ============
void merge(vector<int>& arr, int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;
    vector<int> L(n1), R(n2);
    for (int i = 0; i < n1; i++) L[i] = arr[left + i];
    for (int j = 0; j < n2; j++) R[j] = arr[mid + 1 + j];
    int i = 0, j = 0, k = left;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k++] = L[i++];
        } else {
            arr[k++] = R[j++];
        }
    }
    while (i < n1) arr[k++] = L[i++];
    while (j < n2) arr[k++] = R[j++];
}

void mergeSort(vector<int>& arr, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }
}

void mergeSortWrapper(vector<int>& arr) {
    mergeSort(arr, 0, arr.size() - 1);
}

// ============ Utility Functions ============
void printArray(const vector<int>& arr, const string& name = "") {
    if (!name.empty()) cout << name << ": ";
    for (int num : arr) {
        cout << num << " ";
    }
    cout << endl;
}

vector<int> copyArray(const vector<int>& src) {
    return vector<int>(src.begin(), src.end());
}

vector<int> generateRandomArray(int size, int minVal = 1, int maxVal = 100) {
    vector<int> arr(size);
    random_device rd;
    mt19937 gen(rd());
    uniform_int_distribution<> dis(minVal, maxVal);
    for (int i = 0; i < size; i++) {
        arr[i] = dis(gen);
    }
    return arr;
}

bool isSorted(const vector<int>& arr) {
    for (size_t i = 1; i < arr.size(); i++) {  // 使用 size_t 避免警告
        if (arr[i] < arr[i - 1]) return false;
    }
    return true;
}

void testSortingAlgorithm(void (*sortFunc)(vector<int>&), 
                          vector<int>& arr, 
                          const string& name) {
    vector<int> copy = copyArray(arr);
    
    auto start = high_resolution_clock::now();
    sortFunc(copy);
    auto end = high_resolution_clock::now();
    auto duration = duration_cast<microseconds>(end - start);
    
    cout << name << ":\t";
    if (isSorted(copy)) {
        cout << "OK, ";
    } else {
        cout << "FAIL, ";
    }
    cout << "Time: " << duration.count() << " us";
    cout << endl;
}

// ============ Main Function ============
int main() {
    vector<int> sizes = {10, 100, 1000, 5000, 10000};
    
    for (int size : sizes) {
        cout << "\n========== Data Size: " << size << " ==========" << endl;
        
        vector<int> original = generateRandomArray(size, 1, size * 10);
        
        if (size <= 100) {
            cout << "Original: ";
            printArray(original);
        }
        
        testSortingAlgorithm(bubbleSort, original, "Bubble Sort");
        testSortingAlgorithm(selectionSort, original, "Selection Sort");
        testSortingAlgorithm(quickSortWrapper, original, "Quick Sort");
        testSortingAlgorithm(mergeSortWrapper, original, "Merge Sort");
    }
    
    cout << "\n========== Special Cases ==========" << endl;
    
    vector<int> sorted = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    cout << "Sorted array: ";
    printArray(sorted);
    testSortingAlgorithm(bubbleSort, sorted, "Bubble Sort");
    testSortingAlgorithm(selectionSort, sorted, "Selection Sort");
    testSortingAlgorithm(quickSortWrapper, sorted, "Quick Sort");
    testSortingAlgorithm(mergeSortWrapper, sorted, "Merge Sort");
    
    vector<int> reversed = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
    cout << "\nReversed array: ";
    printArray(reversed);
    testSortingAlgorithm(bubbleSort, reversed, "Bubble Sort");
    testSortingAlgorithm(selectionSort, reversed, "Selection Sort");
    testSortingAlgorithm(quickSortWrapper, reversed, "Quick Sort");
    testSortingAlgorithm(mergeSortWrapper, reversed, "Merge Sort");
    
    vector<int> duplicates = {5, 2, 5, 1, 3, 5, 2, 4, 1, 3};
    cout << "\nDuplicates array: ";
    printArray(duplicates);
    testSortingAlgorithm(bubbleSort, duplicates, "Bubble Sort");
    testSortingAlgorithm(selectionSort, duplicates, "Selection Sort");
    testSortingAlgorithm(quickSortWrapper, duplicates, "Quick Sort");
    testSortingAlgorithm(mergeSortWrapper, duplicates, "Merge Sort");
    
    cout << "\n========== Algorithm Summary ==========" << endl;
    cout << "Algorithm\tTime\t\tSpace\t\tStable" << endl;
    cout << "Bubble Sort\tO(n^2)\t\tO(1)\t\tYes" << endl;
    cout << "Selection Sort\tO(n^2)\t\tO(1)\t\tNo" << endl;
    cout << "Quick Sort\tO(nlogn)\tO(logn)\t\tNo" << endl;
    cout << "Merge Sort\tO(nlogn)\tO(n)\t\tYes" << endl;
    
    system("pause");
    return 0;
}

一个测试

 刻意退化快排后的结果

正序1-10000

Bubble Sort: OK, Time: 36 us
Selection Sort: OK, Time: 187132 us
Quick Sort: OK, Time: 494184 us
Merge Sort: OK, Time: 4682 us

逆序1-10000

Bubble Sort: OK, Time: 560557 us
Selection Sort: OK, Time: 183125 us
Quick Sort: OK, Time: 295989 us
Merge Sort: OK, Time: 4287 us


图片

#include <iostream>
#include <vector>
#include <chrono>
#include <random>
#include <ctime>
#include <algorithm>
#include <fstream>
#include <string>
#include <iomanip>
#include <cmath>
#include <numeric>
#include <direct.h>  // 用于获取当前目录
#include <windows.h> // 用于获取路径
using namespace std;
using namespace chrono;

// ============ 获取当前工作目录(Windows) ============
string getCurrentDirectory() {
    char buffer[MAX_PATH];
    GetCurrentDirectory(MAX_PATH, buffer);
    return string(buffer);
}

// ============ 数据结构体定义 ============
struct DataItem {
    int id;
    int age;
    string name;
    
    DataItem(int _id = 0, int _age = 0, string _name = "") 
    : id(_id), age(_age), name(_name) {}
};

// 按name字典序排序的比较函数
bool compareByName(const DataItem& a, const DataItem& b) {
    return a.name < b.name;
}

// 复制结构体数组
vector<DataItem> copyDataArray(const vector<DataItem>& src) {
    return vector<DataItem>(src.begin(), src.end());
}

// 打印结构体数组(用于调试)
void printDataArray(const vector<DataItem>& arr, const string& name = "") {
    if (!name.empty()) cout << name << ": ";
    for (size_t i = 0; i < min(arr.size(), size_t(10)); i++) {
        cout << "{" << arr[i].id << "," << arr[i].age << ",\"" << arr[i].name << "\"} ";
    }
    if (arr.size() > 10) cout << "...";
    cout << endl;
}

// ============ 1. Bubble Sort (整数版本) ============
void bubbleSortInt(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; i++) {
        bool swapped = false;
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr[j], arr[j + 1]);
                swapped = true;
            }
        }
        if (!swapped) break;
    }
}

// Bubble Sort (结构体版本)
void bubbleSortStruct(vector<DataItem>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; i++) {
        bool swapped = false;
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j + 1].name < arr[j].name) {
                swap(arr[j], arr[j + 1]);
                swapped = true;
            }
        }
        if (!swapped) break;
    }
}

// ============ 2. Selection Sort (整数版本) ============
void selectionSortInt(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; i++) {
        int minIdx = i;
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIdx]) {
                minIdx = j;
            }
        }
        if (minIdx != i) {
            swap(arr[i], arr[minIdx]);
        }
    }
}

// Selection Sort (结构体版本)
void selectionSortStruct(vector<DataItem>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; i++) {
        int minIdx = i;
        for (int j = i + 1; j < n; j++) {
            if (arr[j].name < arr[minIdx].name) {
                minIdx = j;
            }
        }
        if (minIdx != i) {
            swap(arr[i], arr[minIdx]);
        }
    }
}

// ============ 3. Quick Sort (整数版本) ============
int partitionInt(vector<int>& arr, int low, int high) {
    int pivot = arr[high];
    int i = low - 1;
    for (int j = low; j < high; j++) {
        if (arr[j] <= pivot) {
            i++;
            swap(arr[i], arr[j]);
        }
    }
    swap(arr[i + 1], arr[high]);
    return i + 1;
}

void quickSortInt(vector<int>& arr, int low, int high) {
    if (low < high) {
        int pi = partitionInt(arr, low, high);
        quickSortInt(arr, low, pi - 1);
        quickSortInt(arr, pi + 1, high);
    }
}

void quickSortIntWrapper(vector<int>& arr) {
    if (!arr.empty()) {
        quickSortInt(arr, 0, arr.size() - 1);
    }
}

// Quick Sort (结构体版本)
int partitionStruct(vector<DataItem>& arr, int low, int high) {
    DataItem pivot = arr[high];
    int i = low - 1;
    for (int j = low; j < high; j++) {
        if (arr[j].name <= pivot.name) {
            i++;
            swap(arr[i], arr[j]);
        }
    }
    swap(arr[i + 1], arr[high]);
    return i + 1;
}

void quickSortStruct(vector<DataItem>& arr, int low, int high) {
    if (low < high) {
        int pi = partitionStruct(arr, low, high);
        quickSortStruct(arr, low, pi - 1);
        quickSortStruct(arr, pi + 1, high);
    }
}

void quickSortStructWrapper(vector<DataItem>& arr) {
    if (!arr.empty()) {
        quickSortStruct(arr, 0, arr.size() - 1);
    }
}

// ============ 4. Merge Sort (整数版本) ============
void mergeInt(vector<int>& arr, int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;
    vector<int> L(n1), R(n2);
    for (int i = 0; i < n1; i++) L[i] = arr[left + i];
    for (int j = 0; j < n2; j++) R[j] = arr[mid + 1 + j];
    int i = 0, j = 0, k = left;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k++] = L[i++];
        } else {
            arr[k++] = R[j++];
        }
    }
    while (i < n1) arr[k++] = L[i++];
    while (j < n2) arr[k++] = R[j++];
}

void mergeSortInt(vector<int>& arr, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        mergeSortInt(arr, left, mid);
        mergeSortInt(arr, mid + 1, right);
        mergeInt(arr, left, mid, right);
    }
}

void mergeSortIntWrapper(vector<int>& arr) {
    if (!arr.empty()) {
        mergeSortInt(arr, 0, arr.size() - 1);
    }
}

// Merge Sort (结构体版本)
void mergeStruct(vector<DataItem>& arr, int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;
    vector<DataItem> L(n1), R(n2);
    for (int i = 0; i < n1; i++) L[i] = arr[left + i];
    for (int j = 0; j < n2; j++) R[j] = arr[mid + 1 + j];
    int i = 0, j = 0, k = left;
    while (i < n1 && j < n2) {
        if (L[i].name <= R[j].name) {
            arr[k++] = L[i++];
        } else {
            arr[k++] = R[j++];
        }
    }
    while (i < n1) arr[k++] = L[i++];
    while (j < n2) arr[k++] = R[j++];
}

void mergeSortStruct(vector<DataItem>& arr, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        mergeSortStruct(arr, left, mid);
        mergeSortStruct(arr, mid + 1, right);
        mergeStruct(arr, left, mid, right);
    }
}

void mergeSortStructWrapper(vector<DataItem>& arr) {
    if (!arr.empty()) {
        mergeSortStruct(arr, 0, arr.size() - 1);
    }
}

// ============ 数据生成器 ============

// 1. 生成正序数据(已排序)
vector<int> generateSortedData(int size) {
    vector<int> arr(size);
    for (int i = 0; i < size; i++) {
        arr[i] = i + 1;
    }
    return arr;
}

// 2. 生成逆序数据
vector<int> generateReversedData(int size) {
    vector<int> arr(size);
    for (int i = 0; i < size; i++) {
        arr[i] = size - i;
    }
    return arr;
}

// 3. 生成均匀分布数据 [1, size*4]
vector<int> generateUniformData(int size) {
    vector<int> arr(size);
    random_device rd;
    mt19937 gen(rd());
    uniform_int_distribution<> dis(1, size * 4);
    for (int i = 0; i < size; i++) {
        arr[i] = dis(gen);
    }
    return arr;
}

// 4. 生成正态分布数据 (mean=size/2, std=size/5)
vector<int> generateNormalData(int size) {
    vector<int> arr(size);
    random_device rd;
    mt19937 gen(rd());
    double mean = size / 2.0;
    double stddev = size / 5.0;
    normal_distribution<double> dis(mean, stddev);
    for (int i = 0; i < size; i++) {
        double val = dis(gen);
        val = max(1.0, min(double(size), val));
        arr[i] = static_cast<int>(round(val));
    }
    return arr;
}

// 5. 生成随机分布(模拟退火风格)
vector<int> generateRandomData(int size) {
    vector<int> arr(size);
    for (int i = 0; i < size; i++) {
        arr[i] = i + 1;
    }
    
    random_device rd;
    mt19937 gen(rd());
    
    shuffle(arr.begin(), arr.end(), gen);
    
    int numSwaps = size / 10;
    uniform_int_distribution<> dis(0, size - 1);
    for (int i = 0; i < numSwaps; i++) {
        int idx1 = dis(gen);
        int idx2 = dis(gen);
        swap(arr[idx1], arr[idx2]);
    }
    
    return arr;
}

// 生成结构体数组数据
vector<DataItem> generateDataItems(int size, const vector<int>& baseData) {
    vector<DataItem> items(size);
    random_device rd;
    mt19937 gen(rd());
    uniform_int_distribution<> ageDis(1, 100);
    
    string alphabet = "abcdefghijklmnopqrstuvwxyz";
    uniform_int_distribution<> letterDis(0, 25);
    
    for (int i = 0; i < size; i++) {
        items[i].id = i + 1;
        items[i].age = ageDis(gen);
        
        int nameLen = letterDis(gen) % 6 + 3;
        string name;
        for (int j = 0; j < nameLen; j++) {
            name += alphabet[letterDis(gen)];
        }
        items[i].name = name;
    }
    
    vector<DataItem> shuffled = items;
    for (int i = 0; i < size; i++) {
        int idx = baseData[i] % size;
        items[i] = shuffled[idx];
    }
    
    return items;
}

// ============ 验证函数 ============

bool verifySortedInt(const vector<int>& arr) {
    for (size_t i = 1; i < arr.size(); i++) {
        if (arr[i] < arr[i - 1]) return false;
    }
    return true;
}

bool verifySortedStruct(const vector<DataItem>& arr) {
    for (size_t i = 1; i < arr.size(); i++) {
        if (arr[i].name < arr[i - 1].name) return false;
    }
    return true;
}

// ============ 主函数 ============

int main() {
    // 获取当前工作目录
    string currentDir = getCurrentDirectory();
    cout << "当前工作目录: " << currentDir << endl;
    
    // 构建CSV文件路径(直接保存在当前目录)
    string csvPath = "sorting_results.csv";
    
    // 尝试创建CSV文件
    ofstream csvFile(csvPath.c_str());
    if (!csvFile.is_open()) {
        cerr << "错误:无法创建 sorting_results.csv 文件!" << endl;
        cerr << "请检查是否有写入权限,或尝试以管理员身份运行。" << endl;
        
        // 尝试在桌面创建
        string desktopPath = string(getenv("USERPROFILE")) + "\\Desktop\\sorting_results.csv";
        cout << "尝试在桌面创建文件: " << desktopPath << endl;
        csvFile.open(desktopPath.c_str());
        
        if (!csvFile.is_open()) {
            cerr << "仍然无法创建文件。请手动检查权限。" << endl;
            system("pause");
            return 1;
        } else {
            csvPath = desktopPath;
        }
    }
    
    // 数据规模:2^10 到 2^18
    vector<int> sizes;
    for (int i = 10; i <= 16; i++) {
        sizes.push_back(1 << i);
    }
    
    // 数据分布类型
    vector<string> distributions;
    distributions.push_back("Sorted");
    distributions.push_back("Reversed");
    distributions.push_back("Uniform");
    distributions.push_back("Normal");
    distributions.push_back("Random");
    
    // 写入CSV头
    csvFile << "DataSize,Distribution,DataType,BubbleSort(us),SelectionSort(us),QuickSort(us),MergeSort(us),Degradation\n";
    
    cout << "\n========== 开始性能测试 ==========" << endl;
    cout << "测试数据规模: ";
    for (size_t i = 0; i < sizes.size(); i++) {
        cout << sizes[i] << " ";
    }
    cout << endl << endl;
    
    int completedTests = 0;
    
    // 对每个数据规模进行测试
    for (size_t s = 0; s < sizes.size(); s++) {
        int size = sizes[s];
        cout << "========================================" << endl;
        cout << "测试数据规模: " << size << endl;
        cout << "========================================" << endl;
        
        // 生成各种分布的数据
        vector<int> sortedData = generateSortedData(size);
        vector<int> reversedData = generateReversedData(size);
        vector<int> uniformData = generateUniformData(size);
        vector<int> normalData = generateNormalData(size);
        vector<int> randomData = generateRandomData(size);
        
        vector<vector<int> > allData;
        allData.push_back(sortedData);
        allData.push_back(reversedData);
        allData.push_back(uniformData);
        allData.push_back(normalData);
        allData.push_back(randomData);
        
        // 对每种分布进行测试
        for (size_t d = 0; d < distributions.size(); d++) {
            string distName = distributions[d];
            vector<int>& data = allData[d];
            
            cout << "\n  分布: " << distName << endl;
            
            // 测试整数数组
            vector<int> copy1 = data, copy2 = data, copy3 = data, copy4 = data;
            
            auto start = high_resolution_clock::now();
            bubbleSortInt(copy1);
            auto end = high_resolution_clock::now();
            long long t1 = duration_cast<microseconds>(end - start).count();
            
            start = high_resolution_clock::now();
            selectionSortInt(copy2);
            end = high_resolution_clock::now();
            long long t2 = duration_cast<microseconds>(end - start).count();
            
            start = high_resolution_clock::now();
            quickSortIntWrapper(copy3);
            end = high_resolution_clock::now();
            long long t3 = duration_cast<microseconds>(end - start).count();
            
            start = high_resolution_clock::now();
            mergeSortIntWrapper(copy4);
            end = high_resolution_clock::now();
            long long t4 = duration_cast<microseconds>(end - start).count();
            
            // 验证正确性(只对中等规模验证)
            if (size <= 32768) {
                bool v1 = verifySortedInt(copy1);
                bool v2 = verifySortedInt(copy2);
                bool v3 = verifySortedInt(copy3);
                bool v4 = verifySortedInt(copy4);
                
                if (!v1 || !v2 || !v3 || !v4) {
                    cout << "    警告:某些算法排序失败!" << endl;
                }
            }
            
            // 检测退化情况
            string degradation = "None";
            if (distName == "Sorted" && (t1 > 1000000 || t2 > 1000000)) {
                degradation = "Bubble/Selection Degradation on Sorted";
            }
            if (distName == "Reversed" && t3 > 10000000) {
                degradation = "QuickSort Degradation on Reversed";
            }
            
            // 生成结构体数据
            vector<DataItem> structData = generateDataItems(size, data);
            
            // 测试结构体数组
            vector<DataItem> copy1s = structData, copy2s = structData, copy3s = structData, copy4s = structData;
            
            start = high_resolution_clock::now();
            bubbleSortStruct(copy1s);
            end = high_resolution_clock::now();
            long long t1s = duration_cast<microseconds>(end - start).count();
            
            start = high_resolution_clock::now();
            selectionSortStruct(copy2s);
            end = high_resolution_clock::now();
            long long t2s = duration_cast<microseconds>(end - start).count();
            
            start = high_resolution_clock::now();
            quickSortStructWrapper(copy3s);
            end = high_resolution_clock::now();
            long long t3s = duration_cast<microseconds>(end - start).count();
            
            start = high_resolution_clock::now();
            mergeSortStructWrapper(copy4s);
            end = high_resolution_clock::now();
            long long t4s = duration_cast<microseconds>(end - start).count();
            
            // 写入CSV - 整数数据
            csvFile << size << "," << distName << ",Integer,"
            << fixed << setprecision(3) << (double)t1 << ","
            << fixed << setprecision(3) << (double)t2 << ","
            << fixed << setprecision(3) << (double)t3 << ","
            << fixed << setprecision(3) << (double)t4 << ","
            << degradation << "\n";
            
            // 写入CSV - 结构体数据
            csvFile << size << "," << distName << ",Struct,"
            << fixed << setprecision(3) << (double)t1s << ","
            << fixed << setprecision(3) << (double)t2s << ","
            << fixed << setprecision(3) << (double)t3s << ","
            << fixed << setprecision(3) << (double)t4s << ","
            << degradation << "\n";
            
            // 控制台输出
            cout << "    整数数组 - 冒泡: " << t1 << " us, 选择: " << t2 << " us, 快速: " << t3 << " us, 归并: " << t4 << " us" << endl;
            cout << "    结构体数组 - 冒泡: " << t1s << " us, 选择: " << t2s << " us, 快速: " << t3s << " us, 归并: " << t4s << " us" << endl;
            if (degradation != "None") {
                cout << "    警告: " << degradation << endl;
            }
            
            // 刷新CSV缓冲区,确保数据写入
            csvFile.flush();
            
            completedTests++;
        }
        cout << endl;
    }
    
    csvFile.close();
    
    cout << "\n========== 测试完成 ==========" << endl;
    cout << "总共完成 " << completedTests << " 组测试" << endl;
    cout << "结果已保存到: " << csvPath << endl;
    cout << "\n现在运行Python脚本绘制图表:" << endl;
    cout << "python plot_charts.py" << endl;
    
    system("pause");
    return 0;
}

 

posted @ 2026-08-12 10:11  Noname_min  阅读(4)  评论(0)    收藏  举报