/*
K-中心点算法思想: 将n个样本分成k个聚类,每个聚类里的样本关联性(相似性)较强。
假如我现在有5个样本,分别是A,B,C,D,E,他们是二维向量。第一步是随机选择其中两个样本,就选A和B好了,并称他们为中心点,其他的称为非中心点,即C,D,E为非中心点。
第二步,将非中心点归类到距离它最近的那个中心点的聚类。比如现在C归到A的聚类中,D归到B的聚类中,E归到A的聚类中,此时A,C,E是一个聚类,B,D是一个聚类。并计算总的代价(以距离来算)。第三步,用非中心点替换中心点,比如用C替换A,C变成了中心点,A变成了非中心点,然后再计算与之前的代价之差,如果小于0则可替换,否则不可替换。一直迭代下去,直到
所有的中心点都没有发生替换。

*/
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<time.h>
using namespace std;
struct mem{
    bool isMedoid;
    char symbol;
};
struct Node;
typedef struct Node *PNode;
struct Node{  //节点
    mem info;
    PNode link;
};

struct LinkQueue{ //队列
    PNode f;
    PNode r;
};
typedef struct LinkQueue* PLinkQueue;
PLinkQueue createEmptyQueue_link(){ //创建一个空的队列
    PLinkQueue plqu;
    plqu=(PLinkQueue)malloc(sizeof(struct LinkQueue));
    if(plqu!=NULL){
        plqu->f=NULL;
        plqu->r=NULL;
    }
    else cout<<"Out of space"<<endl;
    return plqu;
}
int isEmptyQueue_link(PLinkQueue plqu){
    return  (plqu->f==NULL);
}
void enQueue_Link(PLinkQueue plqu,mem x){
    PNode p;
    p=(PNode)malloc(sizeof(struct Node));
    if(p==NULL) cout<<"Out of space"<<endl;
    else{
        p->info=x;
        p->link=NULL;
        if(plqu->f==NULL) plqu->f=p;
        else plqu->r->link=p;
        plqu->r=p;
    }
}
void deQueue_link(PLinkQueue plqu){
    PNode p;
    if(plqu->f==NULL) cout<<"Empty Queue"<<endl;
    else{
        p=plqu->f;
        cout<<p->info.symbol;
        plqu->f=p->link;
        free(p);
    }
}
void showCase(double linjiebiao[5][5]){
    int i,j;
    char tmp;
    cout<<"目前的邻接矩阵形式如下:"<<endl;
    cout<<"========================================="<<endl;
    cout<<"     A    B    C    D    E    "<<endl;
    for(i=0;i<5;i++){
        tmp='A'+i;
        cout<<tmp;
        for(j=0;j<5;j++) cout<<"    "<<linjiebiao[i][j];
        cout<<endl;
    }
    cout<<"========================================"<<endl;
}
void arbStart(struct mem memlist[5]){ //随机选2个点作为中心点
    int i,j;
    for(i=0;i<5;i++) if(memlist[i].isMedoid!=false) memlist[i].isMedoid=false;
    srand((unsigned)time(NULL));
    i=rand()%5;

    memlist[i].isMedoid=true;
    j=rand()%5;

    while(j==i) j=rand()%5;
    memlist[j].isMedoid=true;
}
double distance(int j,int i,int k,double linjiebiao[5][5]){
    //求解j,i,k中心店中较近的那个点的距离
    if(linjiebiao[j][i]<linjiebiao[j][k]) return linjiebiao[j][i];
    else return linjiebiao[j][k];
}
double TC(int index[2],int i,int h,double linjiebiao[5][5]){
    int j;
    double sum=0;
    int tmp;
    if(i==index[0]) tmp=index[1];
    else if(i==index[1]) tmp=index[0];
    for(j=0;j<5;j++){
        sum+=distance(j,h,tmp,linjiebiao)-distance(j,index[0],index[1],linjiebiao);
    }
    return sum;
}
int smallest_distance_index(int index[2],int h,double linjiebiao[5][5]){
    // 判断点h属于哪个中心点以形成簇
    int i,result=index[0];
    for(i=0;i<2;i++)
        if(linjiebiao[index[i]][h]<linjiebiao[index[0]][h]) result=index[i];
    return result;
}
void showQueue(PLinkQueue pq){
    cout<<"{";
    while(!isEmptyQueue_link(pq)){
        deQueue_link(pq);
        cout<<",";
    }
    cout<<'\b';
    cout<<"}"<<endl;

}
void reposition(mem memlist[5],double linjiebiao[5][5]){
    //重新调整位置
    int count,count1,h,i,k,holdi,holdh,index[2];
    double tempdif;
    bool tmp;
    do{
        count1=0;
        for(k=0;k<5;k++){
            if(memlist[k].isMedoid==true){ //保存中心点
                index[count1]=k;
                count1++;
            }
        }
        count=0;
        for(h=0;h<5;h++){
            for(i=0;i<5;i++){
                if(memlist[h].isMedoid==false&&memlist[i].isMedoid==true){
                    double dif=TC(index,i,h,linjiebiao);
                    if(count==0){
                        tempdif=dif;
                        holdi=i;
                        holdh=h;
                        count++;
                    }
                    else if(dif<tempdif){
                        tempdif=dif;
                        holdi=i;
                        holdh=h;
                        count++;
                    }
                }
            }
        }
        if(tempdif<0){ //小于0可替换
            tmp=memlist[holdi].isMedoid;
            memlist[holdi].isMedoid=memlist[holdh].isMedoid;
            memlist[holdh].isMedoid=tmp;
        }
        else if(tempdif>=0) break;
    }while(1);
}
int main(){
    int i,h,count; //i指代当前中心点,h指代可替换非中心点
    int index[2]; //保存簇中心点
    PLinkQueue pq[2];
    pq[0]=createEmptyQueue_link(); //两个队列
    pq[1]=createEmptyQueue_link();
    double linjiebiao[5][5]={{0,1,2,2,3},{1,0,2,4,3},{2,2,0,1,5},{2,4,1,0,3},{3,3,5,3,0}};
    struct mem memlist[5]={{false,'A'},{false,'B'},{false,'C'},{false,'D'},{false,'E'}};
    showCase(linjiebiao);

    cout<<"期望得到的簇的数目暂定为2为例。"<<endl;
    cout<<endl<<endl<<endl;
    arbStart(memlist);
    cout<<"初始化后的中心点分布情况:"<<endl;

    int k;
    for(k=0;k<5;k++) cout<<memlist[k].symbol<<"    "<<memlist[k].isMedoid<<endl;

    reposition(memlist,linjiebiao);
    cout<<endl<<endl<<endl;

    cout<<"经过K-中心点算法处理后的中心点分布情况:"<<endl;
    for(k=0;k<5;k++) cout<<memlist[k].symbol<<"    "<<memlist[k].isMedoid<<endl;
    cout<<endl<<endl<<endl;

    count=0;
    for(i=0;i<5;i++){
        if(memlist[i].isMedoid==true){
            cout<<memlist[i].symbol<<"是最终得到的中心点"<<endl;
            enQueue_Link(pq[count],memlist[i]);
            index[count]=i;
            count++;
        }
    }
    for(h=0;h<5;h++){
        if(memlist[h].isMedoid==false){
            if(smallest_distance_index(index,h,linjiebiao)==index[0])
                enQueue_Link(pq[0],memlist[h]);
            else if(smallest_distance_index(index,h,linjiebiao)==index[1])
                enQueue_Link(pq[1],memlist[h]);
        }
    }
    cout<<"以以上两个中心点为中心的两个簇为:"<<endl;
    showQueue(pq[0]);
    showQueue(pq[1]);

    return 0;
}