數據結構文章推薦: |
---|
👉 順序表和鏈表實現圖書管理系統 |
👉 指針如何賦值?關於指針的理解 |
👉 深度優先搜索判斷有向圖路徑是否存在 |
👉 待更新 |
文章目錄:
- 課程設計目的
- 課程設計內容要求
- 課程設計代碼實現
- 項目效果展示
- 最後注意事項
一、課程設計目的
1、掌握數據結構課程的基本內容和方法。
2、掌握基於線性表、二叉排序樹和散列表不同存儲結構的查找算法。
3、掌握不同檢索策略對應的平均查找長度ASL的計算方法,明確不同檢索策略的時間性能差別。
4、掌握相關排序算法。
二、課程設計內容要求
一篇英文文章存儲在一個文本文件中,分別基於線性表、二叉排序樹和散列表的不同存儲結構,實現單詞詞頻的的統計和單詞的檢索功能。同時計算不同檢索策略下的ASL,通過比較ASL的大小,對不同檢索策略的時間性能做出相應的比較分析(在課程設計報告中給出)。具體內容如下:
1、一篇包括標點符號的英文文章存儲在文本文件InFile.txt中,假設文件中單詞的個數最多不超過5000個。從該文件中讀取英文單詞,過濾掉所有的標點符號。
2、分別基於線性表、二叉排序樹和散列表的不同存儲結構,實現單詞詞頻的統計和單詞的檢索功能。其中,線性表採用順序表和鏈表兩種不同的存儲結構分別實現順序查找,同時實現基於順序表的折半查找;散列表分別實現基於開放地址法的散列查找和基於鏈地址法的散列查找。
3、不論採用哪種檢索策略,實現的功能均相同。
(1)詞頻統計
當讀取一個單詞後,若該單詞還未出現,則在適當的位置上添加該單詞,將其詞頻計爲1;若該單詞已出現過,則將其詞頻加1. 統計結束後,將所有單詞及其頻率按照字典順序寫入文本文件中。其中,不同的檢索策略分別寫入6個不同的文件:
基於順序表的順序查找:OutFile1.txt
基於鏈表的順序查找:OutFile2.txt
基於順序表的折半查找:OutFile3.txt
基於二叉排序樹的查找:OutFile4.txt
基於開放地址法的散列查找:OutFile5.txt
基於鏈地址法的散列查找:OutFile6.txt
注:如果實現方法正確,6個文件的內容應該一致。
(2)單詞檢索
輸入一個單詞,如果查找成功,則輸出該單詞對應的頻率,同時輸出查找成功的平均查找長度ASL和查找所花的時間。如果查找失敗,則輸出“查找失敗”的提示。
三、課程設計代碼實現
1、詞頻統計檢索系統構思如下:
(一)大體框架:mainView()方法中爲主界面的代碼書寫,在mainView()方法中,包含了用戶選擇功能的算法書寫以及對應功能的調用
(二)結構體:爲實現不同詞頻統計和檢索算法做好準備,其中寫了SqList、LinkList、BiNode、HashTable、HashNode五種結構體分別對應順序表、鏈表、排序二叉樹、哈希表順序存儲、哈希錶鏈式存儲等五種結構體
(三)讀取代碼:分別用了readBySq()、readByLL()、readByTree()、readByHash()、readByHL()來實現順序表讀寫文件、鏈表讀寫文件、排序二叉樹讀寫文件、哈希表順序結構讀取文件、哈希錶鏈式結構讀取文件等五種功能
編譯環境:Codeblocks下新建C++項目進行編寫(個人習慣)
2、詞頻統計檢索系統代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include<math.h>
#define MAX_STORE 5000
/* 順序表存儲結構 */
typedef struct SqList{
char word[30]; //用於存儲單詞,最長30字符
int count = 1; //存儲當前單詞次數
};
/* 鏈表存儲結構 */
typedef struct linkNode{
char word[30]; //用於存儲單詞,最長30字符
int count; //存儲當前單詞次數
linkNode *next; //下一個結點指針
}*LinkList,linkNode;
/* 排序二叉樹存儲結構 */
typedef struct BiNode{
char word[30]; //用於存儲單詞,最長30字符
int count; //用於存儲當前單詞次數
BiNode *lchild,*rchild; //左右孩子指針
}*BiTree,BiNode;
/* 哈希表順序存儲結構 */
typedef struct HashTable{
char word[30]; //用於存儲單詞,最長30字符
int count = 0; //存儲當前單詞次數
};
/* 哈希錶鏈式存儲結構 */
typedef struct HashNode{
char word[30]; //用於存儲單詞,最長30字符
int count = 0; //存儲當前單詞次數
HashNode *next; //下一個結點指針
}*HashLink,HashNode;
/* 所有全局變量的聲明 */
bool isLoad1 = false,isLoad2 = false,isLoad3 = false,isLoad4 = false;//變量與是否進行了文件讀取
int n = 0,num = 0,ASL = -999;
bool flag_word = false;
char buf[MAX_STORE],temp_word[30]; //作爲讀取文件的緩衝區
FILE *filePath; //作爲文件的指針
FILE *fileWrite; //作爲寫文件的指針
int len = 0,i = 0,j = 0,k = 0,x = 0,y = 0; //行字符個數
///1、順序表的聲明
SqList sqList[MAX_STORE];
///2、鏈表的聲明
LinkList linkList = (LinkList)malloc(sizeof(linkNode)); //分配空間
///3、二叉樹的聲明
BiTree tree; //空樹
///4、哈希表的順序存儲結構聲明
HashTable hash[MAX_STORE];
///5、哈希表的鏈式存儲結構聲明
HashLink hashLink[30]; //以單詞長度爲基準
clock_t start,finish; //算法開始和結束時間
//功能一:順序表讀寫詞頻 - 寫入OutFile1.txt
void readBySq(){
if((filePath=fopen("InFile.txt","r"))==NULL){
perror("文件不存在或讀取錯誤!");
exit(1);
}
while(fgets(buf,MAX_STORE,filePath)!=NULL){//逐行讀取
len = strlen(buf); //獲取長度
for(i=0;i<len+1;i++){
if(buf[i]>='a'&&buf[i]<='z'||buf[i]>='A'&&buf[i]<='Z'){
if(buf[i]>='A'&&buf[i]<='Z')buf[i] += 32; //轉換小寫
if(!flag_word)flag_word = true; //標識符轉換
temp_word[j] = buf[i]; //臨時單詞變量賦值
j++; //當前單詞長度++
}else{
if(flag_word){
flag_word=false;//賦值n++;
bool flag_equ=false; //等值標識符
for(x=0;x<n;x++)if(strcmp(temp_word,sqList[x].word)==0){flag_equ=true;sqList[x].count++;break;}
if(!flag_equ){for(k = 0;k<j;k++)sqList[n].word[k] = temp_word[k];n++;}
j = 0;
}memset(temp_word, 0, sizeof(temp_word));
}
}
}
SqList temp;
for(x=0;x<n;x++)//循環排序部分
for(y=0;y<n-x-1;y++){
if(strcmp(sqList[y].word,sqList[y+1].word)>0){
temp = sqList[y];
sqList[y] = sqList[y+1];
sqList[y+1] = temp;
}
}
fileWrite = fopen("OutFile1.txt","w");
for(x=0;x<n;x++)fprintf(fileWrite,"%s %d\n",sqList[x].word,sqList[x].count);
num = n;n = 0;
}
//功能二:鏈表讀寫詞頻 - 寫入OutFile2.txt
void readByLL(){
linkList->next = NULL;
linkNode *p = linkList;linkNode *temp = p;
if((filePath=fopen("InFile.txt","r"))==NULL){
perror("文件不存在或讀取錯誤!");
exit(1);
}
while(fgets(buf,MAX_STORE,filePath)!=NULL){//逐行讀取
len = strlen(buf); //獲取長度
for(i=0;i<len+1;i++){
if(buf[i]>='a'&&buf[i]<='z'||buf[i]>='A'&&buf[i]<='Z'){
if(buf[i]>='A'&&buf[i]<='Z')buf[i] += 32; //轉換小寫
if(!flag_word)flag_word = true; //標識符轉換
temp_word[j] = buf[i]; //臨時單詞變量賦值
j++; //當前單詞長度++
}else{
linkNode *node = (LinkList)malloc(sizeof(linkNode));
node->next = NULL;
if(flag_word){
flag_word=false;
bool flag_equ=false; //等值標識符
while(p){
//printf("%s",p->word);
if(strcmp(p->word,temp_word)==0){p->count++;flag_equ=true;p = linkList;break;}
temp = p;p = p->next;
}p = temp;
if(!flag_equ){strcpy(node->word,temp_word);node->count = 1;p->next = node;n++;}
j = 0;p = linkList->next;
}memset(temp_word, 0, sizeof(temp_word));
}
}
}
for(p=linkList->next;p!=NULL;p=p->next)
for(temp=p->next;temp!=NULL;temp=temp->next){
if(strcmp(p->word,temp->word)>0){
x = p->count;strcpy(temp_word,p->word);
p->count = temp->count;strcpy(p->word,temp->word);
temp->count = x;strcpy(temp->word,temp_word);
}
}memset(temp_word, 0, sizeof(temp_word));
fileWrite = fopen("OutFile2.txt","w");p=linkList->next;num = n;n = 0;
while(p){fprintf(fileWrite,"%s %d\n",p->word,p->count);p=p->next;}
}
//功能三:二叉排序樹讀寫詞頻 - 寫入OutFile3.txt
void readByTree(){
BiNode *p1,*p2; //聲明兩個結點指針
if((filePath=fopen("InFile.txt","r"))==NULL){
perror("文件不存在或讀取錯誤!");
exit(1);
}
while(fgets(buf,MAX_STORE,filePath)!=NULL){//逐行讀取
len = strlen(buf); //獲取長度
for(i=0;i<len+1;i++){
if(buf[i]>='a'&&buf[i]<='z'||buf[i]>='A'&&buf[i]<='Z'){
if(buf[i]>='A'&&buf[i]<='Z')buf[i] += 32; //轉換小寫
if(!flag_word)flag_word = true; //標識符轉換
temp_word[j] = buf[i]; //臨時單詞變量賦值
j++; //當前單詞長度++
}else{
if(flag_word){
flag_word=false;
if(tree == NULL){//先將樹根賦值
tree = (BiTree)malloc(sizeof(BiNode));
strcpy(tree->word,temp_word); //給樹根賦值
tree->count = 1; //給樹根存儲的單詞頻率賦值
tree->lchild = NULL;tree->rchild = NULL; //給兩個子樹賦空
}else{//如果樹根非空,遍歷主體
p1 = tree; //樹根指針
while(strcmp(temp_word,p1->word)!=0){
if(strcmp(temp_word,p1->word)<0&&p1->lchild!=NULL){
p1 = p1->lchild; //p1指向其左孩子
}
if(strcmp(temp_word,p1->word)>0&&p1->rchild!=NULL){
p1 = p1->rchild; //p1指向其右孩子
}
if(strcmp(temp_word,p1->word)<0&&p1->lchild==NULL){
p2 = (BiTree)malloc(sizeof(BiNode)); //創建一個p1的左孩子
p2 ->lchild = NULL;p2->rchild = NULL; //給兩個子樹賦空
strcpy(p2->word,temp_word); //賦值
p2->count = 1;
p1->lchild = p2;
break;
}
if(strcmp(temp_word,p1->word)>0&&p1->rchild==NULL){
p2 = (BiTree)malloc(sizeof(BiNode)); //創建一個p1的右孩子
p2 ->lchild = NULL;p2->rchild = NULL; //給兩個子樹賦空
strcpy(p2->word,temp_word); //賦值
p2->count = 1;
p1->rchild = p2;
break;
}
}if(strcmp(temp_word,p1->word)==0){p1->count++;n++;} //標識符爲真
}
j = 0;p1 = tree; //復原
}memset(temp_word, 0, sizeof(temp_word));
}
}
}
BiNode *st[MAX_STORE];p1 = tree;int top = 0;
fileWrite = fopen("OutFile3.txt","w");
do{
while(p1){
if(top == MAX_STORE)exit(1);//溢出錯誤
st[top++] = p1;
p1 = p1->lchild;
}
if(top){
p1 = st[--top];
//printf("%s ",p1->word); //測試輸出
fprintf(fileWrite,"%s %d\n",p1->word,p1->count);
p1 = p1->rchild;
}
}while(top||p1);num = n;n = 0;
}
//功能四:哈希表順序讀取詞頻
void readByHash(){
if((filePath=fopen("InFile.txt","r"))==NULL){
perror("文件不存在或讀取錯誤!");
exit(1);
}
while(fgets(buf,MAX_STORE,filePath)!=NULL){//逐行讀取
len = strlen(buf); //獲取長度
for(i=0;i<len+1;i++){
if(buf[i]>='a'&&buf[i]<='z'||buf[i]>='A'&&buf[i]<='Z'){
if(buf[i]>='A'&&buf[i]<='Z')buf[i] += 32; //轉換小寫
if(!flag_word)flag_word = true; //標識符轉換
temp_word[j] = buf[i]; //臨時單詞變量賦值
j++; //當前單詞長度++
}else{
if(flag_word){
flag_word=false;//賦值n++;
bool flag_equ=false; //等值標識符
y = 0;
for(x=0;x<j;x++){y += temp_word[x];}
while(hash[y%MAX_STORE].count!=0&&strcmp(hash[y%MAX_STORE].word,temp_word)!=0){y++;if(y>=MAX_STORE)exit(1);}
if(strcmp(hash[y%MAX_STORE].word,temp_word)==0){hash[y%MAX_STORE].count++;flag_equ=true;}
if(!flag_equ){strcpy(hash[y%MAX_STORE].word,temp_word);hash[y%MAX_STORE].count=1;n++;}
j = 0;
}memset(temp_word, 0, sizeof(temp_word));
}
}
}num = n;n=0;
}
//功能五:哈希錶鏈式讀取詞頻
void readByHL(){
HashNode *p = (HashLink)malloc(sizeof(HashNode));
for(i = 0;i<30;i++){hashLink[i]=(HashLink)malloc(sizeof(HashNode));hashLink[i]->count=0;hashLink[i]->next=NULL;}//初始化
if((filePath=fopen("InFile.txt","r"))==NULL){
perror("文件不存在或讀取錯誤!");
exit(1);
}
while(fgets(buf,MAX_STORE,filePath)!=NULL){//逐行讀取
len = strlen(buf); //獲取長度
for(i=0;i<len+1;i++){
if(buf[i]>='a'&&buf[i]<='z'||buf[i]>='A'&&buf[i]<='Z'){
if(buf[i]>='A'&&buf[i]<='Z')buf[i] += 32; //轉換小寫
if(!flag_word)flag_word = true; //標識符轉換
temp_word[j] = buf[i]; //臨時單詞變量賦值
j++; //當前單詞長度++
}else{
if(flag_word){
flag_word=false;
bool flag_equ=false; //等值標識符
y = strlen(temp_word); //獲取單詞長度
HashNode *q = hashLink[y%30];
while(strcmp(q->word,temp_word)!=0&&q->next){q=q->next;}
if(strcmp(q->word,temp_word)==0){q->count++;flag_equ = true;}
if(!flag_equ){p->count=1;strcpy(p->word,temp_word);p->next=NULL;q->next=p;}
j = 0;p = (HashLink)malloc(sizeof(HashNode));
}memset(temp_word, 0, sizeof(temp_word));
}
}
}
}
//系統界面
void mainView(){
linkNode *p = linkList->next;
HashNode *q;
BiNode *p1,*p2; //聲明兩個結點指針
float ASL = 0; //散列表專用的ASl - Σ( ̄。 ̄ノ)
int choice = -999; //用戶的選擇
char str[30]; //用戶查找的單詞
printf("%-30s********************單詞檢索系統********************\n","");
printf("%-34s1、順序表讀寫詞頻");
printf("%-10s2、單鏈表讀寫詞頻\n");
printf("%-34s3、二叉樹讀寫詞頻");
printf("%-10s4、散列表讀取詞頻\n");
printf("%-34s5、查詢單詞及ASL");
printf("%-11s6、退出單詞檢索系統\n");
printf("%-34s系統->請輸入功能:","");
scanf("%d",&choice);
switch(choice){
case 1:
if(isLoad1){printf("%-34s系統->已用順序表讀取,請勿重複讀!\n%-34s","","");}
else{readBySq(); printf("%-34s系統->詞頻統計並寫入成功!\n%-34s","","");}
isLoad1 = true;
system("pause");
system("CLS");
break;
case 2:
if(isLoad2){printf("%-34s系統->已用鏈表讀取,請勿重複讀!\n%-34s","","");}
else{readByLL(); printf("%-34s系統->詞頻統計並寫入成功!\n%-34s","","");}
isLoad2 = true;
system("pause");
system("CLS");
break;
case 3:
if(isLoad3){printf("%-34s系統->已用二叉樹讀取,請勿重複讀!\n%-34s","","");}
else{readByTree(); printf("%-34s系統->詞頻統計並寫入成功!\n%-34s","","");}
isLoad3 = true;
system("pause");
system("CLS");
break;
case 4:
if(isLoad4){printf("%-34s系統->已用散列表讀取,請勿重複讀!\n%-34s","","");}
else{readByHash();readByHL();printf("%-34s系統->詞頻統計並寫入成功!\n%-34s","","");}
isLoad4 = true;
system("pause");
system("CLS");
break;
case 5:
if(!isLoad1&&!isLoad2&&!isLoad3&&!isLoad4){printf("%-34s系統->您還未進行文件讀取!\n%-34s","","");system("pause");system("CLS");}
else{
printf("%-30s====================================================\n","");
printf("%-34s1、順序表順序查找","");
printf("%-10s2、單鏈表順序查找\n","");
printf("%-34s3、順序表折半查找","");
printf("%-10s4、二叉排序樹查找\n","");
printf("%-34s5、開放地址法查找","");
printf("%-10s6、鏈地址法查找\n","");
printf("%-34s7、返回主界面","");
printf("%-14s系統->請輸入功能:","");
scanf("%d",&choice); //輸入選擇
switch(choice){
case 1:
if(!isLoad1){printf("%-34s系統->請先用相應方法讀取!\n%-34s","","");system("pause");break;}
printf("%-30s-------------------------------------------------------\n","");
printf("%-34s系統->請輸入要查找的單詞:","");
scanf("%s",&str);
start = clock();
for(x=0;x<num;x++){
if(strcmp(str,sqList[x].word)==0){finish = clock();printf("%-34s系統->單詞(%s)出現的次數爲:%d次 ASL=(%d/%d) 查找所花時間:%.3f秒\n","",sqList[x].word,sqList[x].count,(num+1),2,(double)(finish-start)/CLOCKS_PER_SEC);break;}
if(strcmp(str,sqList[x].word)!=0&&x==num-1){printf("%-34s系統->未查找到該單詞!\n","");}
}printf("%-34s","");
system("pause");
break;
case 2:
if(!isLoad2){printf("%-34s系統->請先用相應方法讀取!\n%-34s","","");system("pause");break;}
printf("%-30s-------------------------------------------------------\n","");
printf("%-34s系統->請輸入要查找的單詞:","");
scanf("%s",&str);
start = clock();
while(p){
if(strcmp(str,p->word)==0){finish = clock();printf("%-34s系統->單詞(%s)出現的次數爲:%d次 ASL=(%d/%d) 查找所花時間:%.3f秒\n","",p->word,p->count,(num+1),2,(double)(finish-start)/CLOCKS_PER_SEC);break;}
if(strcmp(str,p->word)!=0&&p->next==NULL){printf("%-34s系統->未查找到該單詞!\n","");}p = p->next;
}printf("%-34s","");
system("pause");
break;
case 3:
if(!isLoad1){printf("%-34s系統->請先用相應方法讀取!\n%-34s","","");system("pause");break;}
printf("%-30s-------------------------------------------------------\n","");
printf("%-34s系統->請輸入要查找的單詞:","");
scanf("%s",&str);
start = clock();
x = 0;y = num;k = (x+y)/2;
while(x<=y){
k = (x+y)/2;
if(strcmp(str,sqList[k].word)==0){
finish = clock();
printf("%-34s系統->單詞(%s)出現的次數爲:%d次 ASL=(%d/%d) 查找所花時間:%.3f秒\n","",sqList[k].word,sqList[k].count,(num+1),2,(double)(finish-start)/CLOCKS_PER_SEC);
break;
}else if(strcmp(str,sqList[k].word)>0){x=k+1;}
else if(strcmp(str,sqList[k].word)<0){y=k-1;}
}
if(strcmp(str,sqList[k].word)!=0){printf("%-34s系統->未查找到該單詞!\n","");}printf("%-34s","");
system("pause");
break;
case 4:
if(!isLoad3){printf("%-34s系統->請先用相應方法讀取!\n%-34s","","");system("pause");break;}
printf("%-30s-------------------------------------------------------\n","");
printf("%-34s系統->請輸入要查找的單詞:","");
scanf("%s",&str);
start = clock();
p1 = tree;
while(strcmp(str,p1->word)!=0&&p1){
if(strcmp(str,p1->word)>0){p1 = p1->rchild;}
if(strcmp(str,p1->word)<0){p1 = p1->lchild;}
}
if(strcmp(str,p1->word)==0){
finish = clock();
printf("%-34s系統->單詞(%s)出現的次數爲:%d次 ASL=(%.3f) 查找所花時間:%.3f秒\n","",p1->word,p1->count,log(num+1)/log(2),(double)(finish-start)/CLOCKS_PER_SEC);
}
if(strcmp(str,p1->word)!=0){printf("%-34s系統->未查找到該單詞!\n","");}printf("%-34s","");
system("pause");
break;
case 5:
if(!isLoad4){printf("%-34s系統->請先用相應方法讀取!\n%-34s","","");system("pause");break;}
printf("%-30s-------------------------------------------------------\n","");
printf("%-34s系統->請輸入要查找的單詞:","");
scanf("%s",&str);
start = clock();x = strlen(str);y = 0;bool flag;
for(i=0;i<x;i++){
y+=str[i];
}
ASL = 0.5*(1+MAX_STORE/((MAX_STORE-num)*1.0));//開放地址法ASL計算
while(strcmp(hash[y%MAX_STORE].word,str)!=0){y++;len++;if(y>=MAX_STORE){printf("%-34s系統->未查找到該單詞!\n","");break;}}
if(strcmp(hash[y%MAX_STORE].word,str)==0){finish = clock();printf("%-34s系統->單詞(%s)出現的次數爲:%d次 ASL=(%.3f) 查找所花時間:%f秒\n","",hash[y%MAX_STORE].word,hash[y%MAX_STORE].count,ASL,(double)(finish-start)/CLOCKS_PER_SEC);}
printf("%-34s","");system("pause");
break;
case 6:
if(!isLoad4){printf("%-34s系統->請先用相應方法讀取!\n%-34s","","");system("pause");break;}
printf("%-30s-------------------------------------------------------\n","");
printf("%-34s系統->請輸入要查找的單詞:","");
scanf("%s",&str);
start = clock();x = strlen(str);q = hashLink[x%30];
ASL = (1+num/(MAX_STORE*2.0));//鏈地址法ASL計算
while(strcmp(q->word,str)!=0&&q->next){q=q->next;len++;}
if(strcmp(q->word,str)==0){finish = clock();printf("%-34s系統->單詞(%s)出現的次數爲:%d次 ASL=(%.3f) 查找所花時間:%.3f秒\n","",q->word,q->count,ASL,(double)(finish-start)/CLOCKS_PER_SEC);}
if(strcmp(q->word,str)!=0){printf("%-34s系統->未查找到該單詞!\n","");}
printf("%-34s","");system("pause");
break;
case 7:
break;
default:
printf("%-34s系統->輸入有誤!\n","");system("pause");
break;
}
}
system("CLS");
break;
case 6:
printf("%-34s系統->退出成功,歡迎下次使用!\n","");
exit(0);
default:
printf("%-34s系統->輸入有誤!\n","");system("pause");
break;
}
}
int main()
{
while(true)mainView();
return 0;
}
項目效果展示:
最後注意事項:
1、關於outfile文件是否要輸出6個文件的問題,個人認爲其實輸出3個就夠了,好比開放地址法,關鍵在於查找,而不在於遍歷存儲,純個人理解。
2、完成該系統的過程中,基本都是先準備不同檢索方法的結構體,其次再書寫基於不同結構體的單詞詞頻統計和檢索方法。
3、書寫該系統時,標識符flag的用處非常大,幫助我完成了許多功能的實現,在判斷是否是單詞或者下一個單詞時,都起到了很大作用。
4、該課設代碼中有些變量是我在寫這個項目的時候用處不止一個的,所以可能不好理解,我也沒有修改,請見諒。
5、這是在校期間的一個課設項目,在此做一個簡單的記錄與分享,如果有什麼問題歡迎留言或私信我,謝謝支持!