11장 탐색(Search) 1 – 이진탐색트리(Binary Search Tree)
- 순차 탐색(Linear Search)
- 이진 탐색(Binary Search)
- 이진 탐색의 변형으로 보간 탐색을 들 수 있음
- 보간 탐색(Interpolation Search) : S011_InterpolSearch.c
12345678910111213141516171819202122232425262728293031323334#include <stdio.h>int ISearch(int ar[], int first, int last, int target){int mid;// if(first > last)// return -1; // -1의 반환은 탐색의 실패를 의미if(ar[first] > target || ar[last] < target)return -1;// 이진 탐색과의 차이점을 반영한 문장mid = ((double)(target-ar[first]) / (ar[last]-ar[first]) *(last-first)) + first;if(ar[mid] == target)return mid; // 탐색된 타겟의 인덱스 값 반환else if(target < ar[mid])return ISearch(ar, first, mid-1, target);elsereturn ISearch(ar, mid+1, last, target);}int main(void){int arr[] = {1, 3, 5, 7, 9};int idx;idx = ISearch(arr, 0, sizeof(arr)/sizeof(int)-1, 7); // 7 -> 2로 변경if(idx == -1)printf("탐색 실패 \n");elseprintf("타겟 저장 인덱스: %d \n", idx);return 0;} - 이진 탐색 트리(Binary Search Tree)
- 특징
- 이진 탐색 트리의 노드에 저장된 키(key)는 유일하다
- 루트 노드의 키가 왼쪽 서브 트리를 구성하는 어떠한 노드의 키보다 크다
- 루트 노드의 키가 오른쪽 서브 트리를 구성하는 어떠한 노드의 키보다 작다
- 왼쪽과 오른쪽 서브 트리도 이진 탐색 트리다
- 이진 탐색 트리 연습
- 이진 탐색 트리 생성 : 12, 6, 17, 9, 21, 4, 13, 8, 2, 10
- 이진 탐색 트리에서 다음 노드를 제거한 결과의 이진탐색 트리
- 단말노드 2 제거
- 자식이 1인 17제거
- 자식이 2인 6제거
- 왼쪽 서브 트리중 가장 큰값으로 대체
- 오른쪽 서브 트리중 가장 작은 값으로 대체
- 특징
- 이진 탐색 트리(Binary Search Tree) : S011_BinarySearchTree.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181#include <stdio.h>#include <stdlib.h>typedef struct _bTreeNode{int data;struct _bTreeNode * left;struct _bTreeNode * right;} BTreeNode;////////////////////////// 이진트리 시작BTreeNode * MakeBTreeNode(void){BTreeNode * nd = (BTreeNode*)malloc(sizeof(BTreeNode));nd->left = NULL;nd->right = NULL;return nd;}int GetData(BTreeNode * bt){return bt->data;}void SetData(BTreeNode * bt, int data){bt->data = data;}BTreeNode * GetLeftSubTree(BTreeNode * bt){return bt->left;}BTreeNode * GetRightSubTree(BTreeNode * bt){return bt->right;}void MakeLeftSubTree(BTreeNode * main, BTreeNode * sub){if(main->left != NULL)free(main->left);main->left = sub;}void MakeRightSubTree(BTreeNode * main, BTreeNode * sub){if(main->right != NULL)free(main->right);main->right = sub;}void PreorderTraverse(BTreeNode * bt){if(bt == NULL)return;printf("%d ", bt->data);PreorderTraverse(bt->left);PreorderTraverse(bt->right);}void InorderTraverse(BTreeNode * bt){if(bt == NULL)return;InorderTraverse(bt->left);printf("%d ", bt->data);InorderTraverse(bt->right);}void PostorderTraverse(BTreeNode * bt){if(bt == NULL)return;PostorderTraverse(bt->left);PostorderTraverse(bt->right);printf("%d ", bt->data);}////////////////////////// 이진트리 끝////////////////////////// 이진 탐색 트리 시작void BSTMakeAndInit(BTreeNode ** pRoot){*pRoot = NULL;}int BSTGetNodeData(BTreeNode * bst){return GetData(bst);}void BSTInsert(BTreeNode ** pRoot, int data){BTreeNode * pNode = NULL; // parent nodeBTreeNode * cNode = *pRoot; // current nodeBTreeNode * nNode = NULL; // new node// 새로운 노드가 추가될 위치를 찾는다.while(cNode != NULL){if(data == GetData(cNode))return; // 키의 중복을 허용하지 않음pNode = cNode;if(GetData(cNode) > data)cNode = GetLeftSubTree(cNode);elsecNode = GetRightSubTree(cNode);}// pNode의 서브 노드에 추가할 새 노드의 생성nNode = MakeBTreeNode(); // 새 노드의 생성SetData(nNode, data); // 새 노드에 데이터 저장// pNode의 서브 노드에 새 노드를 추가if(pNode != NULL){ // 새 노드가 루트 노드가 아니라면,if(data < GetData(pNode))MakeLeftSubTree(pNode, nNode);elseMakeRightSubTree(pNode, nNode);}else{ // 새 노드가 루트 노드라면,*pRoot = nNode;}}BTreeNode * BSTSearch(BTreeNode * bst, int target){BTreeNode * cNode = bst; // current nodeint cd; // current datawhile(cNode != NULL){cd = GetData(cNode);if(target == cd)return cNode;else if(target < cd)cNode = GetLeftSubTree(cNode);elsecNode = GetRightSubTree(cNode);}return NULL;}////////////////////////// 이진 탐색 트리 끝///////////////////////// 메인 함수 시작int main(void){BTreeNode * bstRoot;BTreeNode * sNode; // search nodeBSTMakeAndInit(&bstRoot);BSTInsert(&bstRoot, 9);BSTInsert(&bstRoot, 1);BSTInsert(&bstRoot, 6);BSTInsert(&bstRoot, 2);BSTInsert(&bstRoot, 8);// BSTInsert(&bstRoot, 4);BSTInsert(&bstRoot, 3);BSTInsert(&bstRoot, 5);// BSTInsert(&bstRoot, 7);sNode = BSTSearch(bstRoot, 1);if(sNode == NULL)printf("탐색 실패 \n");elseprintf("탐색에 성공한 키의 값: %d \n", BSTGetNodeData(sNode));sNode = BSTSearch(bstRoot, 4);if(sNode == NULL)printf("탐색 실패 \n");elseprintf("탐색에 성공한 키의 값: %d \n", BSTGetNodeData(sNode));sNode = BSTSearch(bstRoot, 6);if(sNode == NULL)printf("탐색 실패 \n");elseprintf("탐색에 성공한 키의 값: %d \n", BSTGetNodeData(sNode));sNode = BSTSearch(bstRoot, 7);if(sNode == NULL)printf("탐색 실패 \n");elseprintf("탐색에 성공한 키의 값: %d \n", BSTGetNodeData(sNode));return 0;} - 이진 탐색 트리(삭제 추가) : S011_BinarySearchTreeRemoveAdd.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300#include <stdio.h>#include <stdlib.h>typedef struct _bTreeNode{int data;struct _bTreeNode * left;struct _bTreeNode * right;} BTreeNode;////////////////////////// 이진트리 시작BTreeNode * MakeBTreeNode(void){BTreeNode * nd = (BTreeNode*)malloc(sizeof(BTreeNode));nd->left = NULL;nd->right = NULL;return nd;}int GetData(BTreeNode * bt){return bt->data;}void SetData(BTreeNode * bt, int data){bt->data = data;}BTreeNode * GetLeftSubTree(BTreeNode * bt){return bt->left;}BTreeNode * GetRightSubTree(BTreeNode * bt){return bt->right;}void MakeLeftSubTree(BTreeNode * main, BTreeNode * sub){if(main->left != NULL)free(main->left);main->left = sub;}void MakeRightSubTree(BTreeNode * main, BTreeNode * sub){if(main->right != NULL)free(main->right);main->right = sub;}void PreorderTraverse(BTreeNode * bt){if(bt == NULL)return;printf("%d ", bt->data);PreorderTraverse(bt->left);PreorderTraverse(bt->right);}void InorderTraverse(BTreeNode * bt){if(bt == NULL)return;InorderTraverse(bt->left);printf("%d ", bt->data);InorderTraverse(bt->right);}void PostorderTraverse(BTreeNode * bt){if(bt == NULL)return;PostorderTraverse(bt->left);PostorderTraverse(bt->right);printf("%d ", bt->data);}BTreeNode * RemoveLeftSubTree(BTreeNode * bt){BTreeNode * delNode;if(bt != NULL) {delNode = bt->left;bt->left = NULL;}return delNode;}BTreeNode * RemoveRightSubTree(BTreeNode * bt){BTreeNode * delNode;if(bt != NULL) {delNode = bt->right;bt->right = NULL;}return delNode;}void ChangeLeftSubTree(BTreeNode * main, BTreeNode * sub){main->left = sub;}void ChangeRightSubTree(BTreeNode * main, BTreeNode * sub){main->right = sub;}////////////////////////// 이진트리 끝////////////////////////// 이진 탐색 트리 시작void BSTMakeAndInit(BTreeNode ** pRoot){*pRoot = NULL;}int BSTGetNodeData(BTreeNode * bst){return GetData(bst);}void BSTInsert(BTreeNode ** pRoot, int data){BTreeNode * pNode = NULL; // parent nodeBTreeNode * cNode = *pRoot; // current nodeBTreeNode * nNode = NULL; // new node// 새로운 노드가 추가될 위치를 찾는다.while(cNode != NULL){if(data == GetData(cNode))return; // 키의 중복을 허용하지 않음pNode = cNode;if(GetData(cNode) > data)cNode = GetLeftSubTree(cNode);elsecNode = GetRightSubTree(cNode);}// pNode의 서브 노드에 추가할 새 노드의 생성nNode = MakeBTreeNode(); // 새 노드의 생성SetData(nNode, data); // 새 노드에 데이터 저장// pNode의 서브 노드에 새 노드를 추가if(pNode != NULL){ // 새 노드가 루트 노드가 아니라면,if(data < GetData(pNode))MakeLeftSubTree(pNode, nNode);elseMakeRightSubTree(pNode, nNode);}else{ // 새 노드가 루트 노드라면,*pRoot = nNode;}}BTreeNode * BSTSearch(BTreeNode * bst, int target){BTreeNode * cNode = bst; // current nodeint cd; // current datawhile(cNode != NULL){cd = GetData(cNode);if(target == cd)return cNode;else if(target < cd)cNode = GetLeftSubTree(cNode);elsecNode = GetRightSubTree(cNode);}return NULL;}BTreeNode * BSTRemove(BTreeNode ** pRoot, int target){// 삭제 대상이 루트 노드인 경우를 별도로 고려해야 한다.BTreeNode * pVRoot = MakeBTreeNode(); // 가상 루트 노드;BTreeNode * pNode = pVRoot; // parent nodeBTreeNode * cNode = *pRoot; // current nodeBTreeNode * dNode; // delete node// 루트 노드를 pVRoot가 가리키는 노드의 오른쪽 서브 노드가 되게 한다.ChangeRightSubTree(pVRoot, *pRoot);// 삭제 대상을 저장한 노드 탐색while(cNode != NULL && GetData(cNode) != target){pNode = cNode;if(target < GetData(cNode))cNode = GetLeftSubTree(cNode);elsecNode = GetRightSubTree(cNode);}if(cNode == NULL) // 삭제 대상이 존재하지 않는다면,return NULL;dNode = cNode; // 삭제 대상을 dNode가 가리키게 한다.// 첫 번째 경우: 삭제할 노드가 단말 노드인 경우if(GetLeftSubTree(dNode) == NULL && GetRightSubTree(dNode) == NULL){if(GetLeftSubTree(pNode) == dNode)RemoveLeftSubTree(pNode);elseRemoveRightSubTree(pNode);}// 두 번째 경우: 하나의 자식 노드를 갖는 경우else if(GetLeftSubTree(dNode) == NULL || GetRightSubTree(dNode) == NULL){BTreeNode * dcNode; // delete node의 자식 노드if(GetLeftSubTree(dNode) != NULL)dcNode = GetLeftSubTree(dNode);elsedcNode = GetRightSubTree(dNode);if(GetLeftSubTree(pNode) == dNode)ChangeLeftSubTree(pNode, dcNode);elseChangeRightSubTree(pNode, dcNode);}// 세 번째 경우: 두 개의 자식 노드를 모두 갖는 경우else{BTreeNode * mNode = GetRightSubTree(dNode); // mininum nodeBTreeNode * mpNode = dNode; // mininum node의 부모 노드int delData;// 삭제할 노드를 대체할 노드를 찾는다.while(GetLeftSubTree(mNode) != NULL){mpNode = mNode;mNode = GetLeftSubTree(mNode);}// 대체할 노드에 저장된 값을 삭제할 노드에 대입한다.delData = GetData(dNode); // 대입 전 데이터 백업SetData(dNode, GetData(mNode));// 대체할 노드의 부모 노드와 자식 노드를 연결한다.if(GetLeftSubTree(mpNode) == mNode)ChangeLeftSubTree(mpNode, GetRightSubTree(mNode));elseChangeRightSubTree(mpNode, GetRightSubTree(mNode));dNode = mNode;SetData(dNode, delData); // 백업 데이터 복원}// 삭제된 노드가 루트 노드인 경우에 대한 처리if(GetRightSubTree(pVRoot) != *pRoot)*pRoot = GetRightSubTree(pVRoot);free(pVRoot);return dNode;}void ShowIntData(int data){printf("%d ", data);}void BSTShowAll(BTreeNode * bst){InorderTraverse(bst);}////////////////////////// 이진 탐색 트리 끝///////////////////////// 메인 함수 시작int main(void){BTreeNode * bstRoot;BTreeNode * sNode; // search nodeBSTMakeAndInit(&bstRoot);BSTInsert(&bstRoot, 5);BSTInsert(&bstRoot, 8);BSTInsert(&bstRoot, 1);BSTInsert(&bstRoot, 6);BSTInsert(&bstRoot, 4);BSTInsert(&bstRoot, 9);BSTInsert(&bstRoot, 3);BSTInsert(&bstRoot, 2);BSTInsert(&bstRoot, 7);BSTShowAll(bstRoot); printf("\n");sNode = BSTRemove(&bstRoot, 3);free(sNode);BSTShowAll(bstRoot); printf("\n");sNode = BSTRemove(&bstRoot, 8);free(sNode);BSTShowAll(bstRoot); printf("\n");sNode = BSTRemove(&bstRoot, 1);free(sNode);BSTShowAll(bstRoot); printf("\n");sNode = BSTRemove(&bstRoot, 6);free(sNode);BSTShowAll(bstRoot); printf("\n");return 0;}